diff --git a/DEPS b/DEPS
index 4e7e483..2d9ecece5 100644
--- a/DEPS
+++ b/DEPS
@@ -307,7 +307,7 @@
   # 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': '78c0f267c137a64fa233a198496eef10470b9e78',
+  'skia_revision': '4cf2c682d5a75a8505633beacf1116f34829254d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -315,7 +315,7 @@
   # 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': '1acf75726c9044e448c7415b5ad602f11df68fc8',
+  'angle_revision': '9f693aa383b2d3ef32179cfefa253db5ddb8c1bb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -386,7 +386,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': 'c307f39005a4c3ce708b8177148b099854a4de9c',
+  'devtools_frontend_revision': 'a2189961b6f7f52d2154aa46d421c1ef594c1538',
   # 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.
@@ -817,7 +817,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    '4eee750653bada2db85ea07433aeceb8af486db5',
+    'f148bb485a5fb167266582ad9b257aa4739a9604',
     'condition': 'checkout_android and checkout_src_internal and not checkout_clank_via_src_internal',
   },
 
@@ -1216,7 +1216,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1cf7275d263df398ffe8ddb5560910fb4a71d874',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f22595bdc3d7cc620d7310b3e2ba40e23448e806',
       'condition': 'checkout_chromeos',
   },
 
@@ -1250,7 +1250,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '97f91787281d208a642e37952e3fc2719af49443',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '1a9a2794f54b2b05d8260bf4b4f2ba7ef75d4585',
     'condition': 'checkout_src_internal',
   },
 
@@ -1645,7 +1645,7 @@
   },
 
   'src/third_party/openh264/src':
-    Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'fac04ceb3e966f613ed17e98178e9d690280bba6',
+    Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'db956674bbdfbaab5acdd3fdb4117c2fef5527e9',
 
   'src/third_party/openscreen/src':
     Var('chromium_git') + '/openscreen' + '@' + '9be5eefa2605408a671cc11c695849400caecbbb',
@@ -1665,7 +1665,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cb8281edbb0e244234ce27dbf69b71c1334bc836',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0cbb37ad93ee2bfcf4c1b0bc3e136eadbe4fb467',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1719,7 +1719,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'nZ7XBCAvVQaWVGQ5BLbVxRo-_KDC3r-9kQTcPquc2t8C',
+              'version': 'zoicZU9zhNbJmgq7fa0FZxcPTPlmRi907Rn2MZ21rAsC',
           },
       ],
       'condition': 'checkout_android',
@@ -1847,10 +1847,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e113fd4de43cad41296384876a20b5937c117438',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0f0cf479db365038b57a90403c3df7bc12937a7f',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'af512281b1b35759155b4151e8bf83467c28533c',
+    Var('webrtc_git') + '/src.git' + '@' + '4a3f26198d094c7b2cf0f65b755f33f20916c66c',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1920,7 +1920,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0b1180978e38034fbe6694690ed0018a16fee5b4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3d3673317c682c363504cc9cc07285884922527b',
     'condition': 'checkout_src_internal',
   },
 
@@ -1950,7 +1950,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '08bGj6dkBPAAsEKExEeVdDsCNDOXVDGkhFy_Rd7t4n8C',
+        'version': 'GbzYVUZYWJYHgcr2RXp9eqqN6-oAX2Karf0qE58fvHoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1961,7 +1961,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'vGJzNTqAywikH3aqgvFEvCcDhs2n4qH_TBGyglkgReMC',
+        'version': '4jOgw4WTfNaMhARYeJ2738V8-pO80fUOjQIJ8jY-iJgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/gfx/browser_view_renderer.cc b/android_webview/browser/gfx/browser_view_renderer.cc
index 54066e4..fed4a4f 100644
--- a/android_webview/browser/gfx/browser_view_renderer.cc
+++ b/android_webview/browser/gfx/browser_view_renderer.cc
@@ -193,7 +193,7 @@
       external_draw_constraints_.transform;
 
   gfx::Rect viewport_rect_for_tile_priority_in_view_space;
-  gfx::Transform screen_to_view(gfx::Transform::kSkipInitialization);
+  gfx::Transform screen_to_view;
   if (transform_for_tile_priority.GetInverse(&screen_to_view)) {
     // Convert from screen space to view space.
     viewport_rect_for_tile_priority_in_view_space =
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index dc401063..4e49373 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -538,8 +538,6 @@
     "focus_cycler.h",
     "frame/frame_context_menu_controller.cc",
     "frame/frame_context_menu_controller.h",
-    "frame/header_view.cc",
-    "frame/header_view.h",
     "frame/non_client_frame_view_ash.cc",
     "frame/non_client_frame_view_ash.h",
     "frame/snap_controller_impl.cc",
diff --git a/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc b/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc
index a864ffd..a34cdd7 100644
--- a/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc
+++ b/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc
@@ -793,10 +793,8 @@
           display::Screen::GetScreen()->GetDisplayNearestWindow(root_window_);
       gfx::Transform rotation_transform;
       rotation_transform.Rotate(display.PanelRotationAsDegree());
-      gfx::Transform rotation_inverse_transform;
-      const bool result =
-          rotation_transform.GetInverse(&rotation_inverse_transform);
-      DCHECK(result);
+      gfx::Transform rotation_inverse_transform =
+          rotation_transform.GetCheckedInverse();
       gfx::PointF scroll = rotation_inverse_transform.MapPoint(
           gfx::PointF(details.scroll_x(), details.scroll_y()));
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 8d394c4..a84fb82 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -207,31 +207,6 @@
   return Shell::Get()->session_controller()->GetLastActiveUserPrefService();
 }
 
-int GetSuggestedContentInfoShownCount() {
-  PrefService* prefs = GetLastActiveUserPrefService();
-  return prefs->GetInteger(prefs::kSuggestedContentInfoShownInLauncher);
-}
-
-void SetSuggestedContentInfoShownCount(int count) {
-  PrefService* prefs = GetLastActiveUserPrefService();
-  prefs->SetInteger(prefs::kSuggestedContentInfoShownInLauncher, count);
-}
-
-bool IsSuggestedContentInfoDismissed() {
-  PrefService* prefs = GetLastActiveUserPrefService();
-  return prefs->GetBoolean(prefs::kSuggestedContentInfoDismissedInLauncher);
-}
-
-void SetSuggestedContentInfoDismissed() {
-  PrefService* prefs = GetLastActiveUserPrefService();
-  prefs->SetBoolean(prefs::kSuggestedContentInfoDismissedInLauncher, true);
-}
-
-bool IsSuggestedContentEnabled() {
-  PrefService* prefs = GetLastActiveUserPrefService();
-  return prefs->GetBoolean(chromeos::prefs::kSuggestedContentEnabled);
-}
-
 // Gets the MRU window shown over the applist when in tablet mode.
 // Returns nullptr if no windows are shown over the applist.
 aura::Window* GetTopVisibleWindow() {
@@ -329,12 +304,6 @@
 
 // static
 void AppListControllerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterIntegerPref(
-      prefs::kSuggestedContentInfoShownInLauncher, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kSuggestedContentInfoDismissedInLauncher, false,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
   registry->RegisterBooleanPref(
       prefs::kLauncherFeedbackOnContinueSectionSent, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
@@ -1390,13 +1359,6 @@
     client_->OnSearchResultVisibilityChanged(id, visibility);
 }
 
-void AppListControllerImpl::MaybeIncreaseSuggestedContentInfoShownCount() {
-  if (ShouldShowSuggestedContentInfo()) {
-    const int count = GetSuggestedContentInfoShownCount();
-    SetSuggestedContentInfoShownCount(count + 1);
-  }
-}
-
 bool AppListControllerImpl::IsAssistantAllowedAndEnabled() const {
   if (!Shell::Get()->assistant_controller()->IsAssistantReady())
     return false;
@@ -1407,27 +1369,6 @@
          state->assistant_status() != assistant::AssistantStatus::NOT_READY;
 }
 
-bool AppListControllerImpl::ShouldShowSuggestedContentInfo() const {
-  if (!IsSuggestedContentEnabled()) {
-    // Don't show if user has interacted with the setting already.
-    SetSuggestedContentInfoDismissed();
-    return false;
-  }
-
-  if (IsSuggestedContentInfoDismissed()) {
-    return false;
-  }
-
-  const int count = GetSuggestedContentInfoShownCount();
-  constexpr int kThresholdToShow = 3;
-  return count >= 0 && count <= kThresholdToShow;
-}
-
-void AppListControllerImpl::MarkSuggestedContentInfoDismissed() {
-  // User dismissed the privacy info view. Will not show the view again.
-  SetSuggestedContentInfoDismissed();
-}
-
 void AppListControllerImpl::OnStateTransitionAnimationCompleted(
     AppListViewState state,
     bool was_animation_interrupted) {
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index cb2383e..9fce936 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -182,10 +182,7 @@
   AssistantViewDelegate* GetAssistantViewDelegate() override;
   void OnSearchResultVisibilityChanged(const std::string& id,
                                        bool visibility) override;
-  void MaybeIncreaseSuggestedContentInfoShownCount() override;
   bool IsAssistantAllowedAndEnabled() const override;
-  bool ShouldShowSuggestedContentInfo() const override;
-  void MarkSuggestedContentInfoDismissed() override;
   void OnStateTransitionAnimationCompleted(
       AppListViewState state,
       bool was_animation_interrupted) override;
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index 008a32d..faa4fde 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -3210,10 +3210,6 @@
   ResultSelectionController* result_selection_controller =
       GetResultSelectionController();
 
-  // Mark the suggested content info as dismissed so that it does not interfere
-  // with the layout for the selection traversal.
-  Shell::Get()->app_list_controller()->MarkSuggestedContentInfoDismissed();
-
   // Add search results to the search model.
   SearchModel* search_model = GetSearchModel();
   search_model->results()->Add(CreateOmniboxSuggestionResult("Suggestion1"));
diff --git a/ash/app_list/app_list_test_view_delegate.cc b/ash/app_list/app_list_test_view_delegate.cc
index 7aefc5f..036c2f9 100644
--- a/ash/app_list/app_list_test_view_delegate.cc
+++ b/ash/app_list/app_list_test_view_delegate.cc
@@ -88,11 +88,6 @@
   is_tablet_mode_ = is_tablet_mode;
 }
 
-void AppListTestViewDelegate::SetShouldShowSuggestedContentInfo(
-    bool should_show) {
-  should_show_suggested_content_info_ = should_show;
-}
-
 void AppListTestViewDelegate::ActivateItem(
     const std::string& id,
     int event_flags,
@@ -173,20 +168,10 @@
     const std::string& id,
     bool visibility) {}
 
-void AppListTestViewDelegate::MaybeIncreaseSuggestedContentInfoShownCount() {}
-
 bool AppListTestViewDelegate::IsAssistantAllowedAndEnabled() const {
   return false;
 }
 
-bool AppListTestViewDelegate::ShouldShowSuggestedContentInfo() const {
-  return should_show_suggested_content_info_;
-}
-
-void AppListTestViewDelegate::MarkSuggestedContentInfoDismissed() {
-  should_show_suggested_content_info_ = false;
-}
-
 void AppListTestViewDelegate::OnStateTransitionAnimationCompleted(
     AppListViewState state,
     bool was_animation_interrupted) {}
diff --git a/ash/app_list/app_list_test_view_delegate.h b/ash/app_list/app_list_test_view_delegate.h
index b6c6c8f..9e9bfd35 100644
--- a/ash/app_list/app_list_test_view_delegate.h
+++ b/ash/app_list/app_list_test_view_delegate.h
@@ -58,9 +58,6 @@
   // Set whether tablet mode is enabled.
   void SetIsTabletModeEnabled(bool is_tablet_mode);
 
-  // Set whether the suggested content info should be shown.
-  void SetShouldShowSuggestedContentInfo(bool should_show);
-
   // AppListViewDelegate overrides:
   bool KeyboardTraversalEngaged() override;
   void StartAssistant() override {}
@@ -97,10 +94,7 @@
   ash::AssistantViewDelegate* GetAssistantViewDelegate() override;
   void OnSearchResultVisibilityChanged(const std::string& id,
                                        bool visibility) override;
-  void MaybeIncreaseSuggestedContentInfoShownCount() override;
   bool IsAssistantAllowedAndEnabled() const override;
-  bool ShouldShowSuggestedContentInfo() const override;
-  void MarkSuggestedContentInfoDismissed() override;
   void OnStateTransitionAnimationCompleted(
       AppListViewState state,
       bool was_animation_interrupted) override;
@@ -145,7 +139,6 @@
   AppListState app_list_page_ = AppListState::kInvalidState;
   AppListViewState app_list_view_state_ = AppListViewState::kClosed;
   bool is_tablet_mode_ = false;
-  bool should_show_suggested_content_info_ = false;
   std::map<size_t, int> open_search_result_counts_;
   AppListModelProvider model_provider_;
   std::unique_ptr<AppListTestModel> model_;
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index a091a4a..9f3d9935 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -143,20 +143,9 @@
   virtual void OnSearchResultVisibilityChanged(const std::string& id,
                                                bool visibility) = 0;
 
-  // If the |prefs::kSuggestedContentInfoShownInLauncher| value is in the range
-  // of allowed values, we will increment it.
-  virtual void MaybeIncreaseSuggestedContentInfoShownCount() = 0;
-
   // Returns true if the Assistant feature is allowed and enabled.
   virtual bool IsAssistantAllowedAndEnabled() const = 0;
 
-  // Returns true if the Suggested Content privacy info view should be shown.
-  virtual bool ShouldShowSuggestedContentInfo() const = 0;
-
-  // Called when close button in the Suggested Content privacy info view is
-  // pressed to indicate not to show the view any more.
-  virtual void MarkSuggestedContentInfoDismissed() = 0;
-
   // Gets the app list page currently shown in the fullscreen app list, as
   // reported from the app list view using `OnAppListPageChanged()`.
   virtual AppListState GetCurrentAppListPage() const = 0;
diff --git a/ash/app_list/model/app_list_model_unittest.cc b/ash/app_list/model/app_list_model_unittest.cc
index 9e251f5..d64a570 100644
--- a/ash/app_list/model/app_list_model_unittest.cc
+++ b/ash/app_list/model/app_list_model_unittest.cc
@@ -270,13 +270,8 @@
   model_->MergeItems(item0->id(), folder->id());
 }
 
-// Same test as above, but for ProductivityLauncher config types.
-TEST_F(AppListModelFolderTest,
-       NonSharedConfigIconGenerationProductivityLauncher) {
-  // The configs tested here are only used by ProductivityLauncher.
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(features::kProductivityLauncher);
-
+// Tests Icon generation configuration for folders on different grid types.
+TEST_F(AppListModelFolderTest, NonSharedConfigIconGeneration) {
   // Ensure any configs set by previous tests are cleared.
   AppListConfigProvider::Get().ResetForTesting();
 
diff --git a/ash/app_list/views/app_list_folder_view_unittest.cc b/ash/app_list/views/app_list_folder_view_unittest.cc
index 2698d2f..660dd47 100644
--- a/ash/app_list/views/app_list_folder_view_unittest.cc
+++ b/ash/app_list/views/app_list_folder_view_unittest.cc
@@ -11,21 +11,19 @@
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/app_list/views/app_list_a11y_announcer.h"
 #include "ash/app_list/views/scrollable_apps_grid_view.h"
-#include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/bind.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/scroll_view.h"
 
 namespace ash {
 
-class AppListFolderViewProductivityLauncherTest : public AshTestBase {
+class AppListFolderViewTest : public AshTestBase {
  public:
-  AppListFolderViewProductivityLauncherTest() = default;
-  ~AppListFolderViewProductivityLauncherTest() override = default;
+  AppListFolderViewTest() = default;
+  ~AppListFolderViewTest() override = default;
 
   // testing::Test:
   void SetUp() override {
@@ -37,13 +35,11 @@
         /*profile_id=*/1, app_list_test_model_.get(), search_model_.get());
   }
 
-  base::test::ScopedFeatureList feature_list_{features::kProductivityLauncher};
   std::unique_ptr<test::AppListTestModel> app_list_test_model_;
   std::unique_ptr<SearchModel> search_model_;
 };
 
-TEST_F(AppListFolderViewProductivityLauncherTest,
-       ScrollViewSizeIsCappedForLargeFolders) {
+TEST_F(AppListFolderViewTest, ScrollViewSizeIsCappedForLargeFolders) {
   // Create a large number of apps, more than a 4 rows.
   app_list_test_model_->CreateAndPopulateFolderWithApps(30);
 
@@ -65,8 +61,7 @@
   EXPECT_LT(scroll_view->height(), tile_height * 5);
 }
 
-TEST_F(AppListFolderViewProductivityLauncherTest,
-       CloseFolderMakesA11yAnnouncement) {
+TEST_F(AppListFolderViewTest, CloseFolderMakesA11yAnnouncement) {
   // Create a folder with a couple items.
   app_list_test_model_->CreateAndPopulateFolderWithApps(2);
 
diff --git a/ash/app_list/views/app_list_item_view_unittest.cc b/ash/app_list/views/app_list_item_view_unittest.cc
index 7c03b33..6f6857f 100644
--- a/ash/app_list/views/app_list_item_view_unittest.cc
+++ b/ash/app_list/views/app_list_item_view_unittest.cc
@@ -9,10 +9,8 @@
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/app_list/views/paged_apps_grid_view.h"
 #include "ash/app_list/views/scrollable_apps_grid_view.h"
-#include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/views/controls/label.h"
@@ -48,18 +46,7 @@
   std::unique_ptr<SearchModel> search_model_;
 };
 
-// Tests with ProductivityLauncher enabled.
-class AppListItemViewProductivityLauncherTest : public AppListItemViewTest {
- public:
-  AppListItemViewProductivityLauncherTest() {
-    feature_list_.InitAndEnableFeature(features::kProductivityLauncher);
-  }
-  ~AppListItemViewProductivityLauncherTest() override = default;
-
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(AppListItemViewProductivityLauncherTest, NewInstallDot) {
+TEST_F(AppListItemViewTest, NewInstallDot) {
   AppListItem* item = CreateAppListItem("Google Buzz");
   ASSERT_FALSE(item->is_new_install());
 
@@ -89,7 +76,7 @@
       "New install");
 }
 
-TEST_F(AppListItemViewProductivityLauncherTest, LabelInsetWithNewInstallDot) {
+TEST_F(AppListItemViewTest, LabelInsetWithNewInstallDot) {
   AppListItem* long_item = CreateAppListItem("Very very very very long name");
   long_item->SetIsNewInstall(true);
   AppListItem* short_item = CreateAppListItem("Short");
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 7d1e01eb..de549e9 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -737,20 +737,6 @@
   }
 }
 
-void AppListView::MaybeIncreasePrivacyInfoRowShownCounts(
-    AppListViewState new_state) {
-  AppListStateTransitionSource transition =
-      GetAppListStateTransitionSource(new_state);
-  switch (transition) {
-    case kFullscreenAllAppsToFullscreenSearch:
-      if (app_list_main_view()->contents_view()->IsShowingSearchResults())
-        delegate_->MaybeIncreaseSuggestedContentInfoShownCount();
-      break;
-    default:
-      break;
-  }
-}
-
 void AppListView::RecordStateTransitionForUma(AppListViewState new_state) {
   AppListStateTransitionSource transition =
       GetAppListStateTransitionSource(new_state);
@@ -1005,7 +991,6 @@
   state_transition_notifier_->Reset(new_state);
 
   StartAnimationForState(new_state);
-  MaybeIncreasePrivacyInfoRowShownCounts(new_state);
   RecordStateTransitionForUma(new_state);
   app_list_state_ = new_state;
   if (delegate_)
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h
index c9ff9cf..034bced 100644
--- a/ash/app_list/views/app_list_view.h
+++ b/ash/app_list/views/app_list_view.h
@@ -339,8 +339,6 @@
   // in progress it will be interrupted.
   void StartAnimationForState(AppListViewState new_state);
 
-  void MaybeIncreasePrivacyInfoRowShownCounts(AppListViewState new_state);
-
   // Applies a bounds animation on this views layer.
   void ApplyBoundsAnimation(AppListViewState target_state,
                             base::TimeDelta duration_ms);
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index 6155cad..c1ee1d5 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -507,17 +507,6 @@
   keyboard::KeyboardUIController keyboard_ui_controller_;
 };
 
-// Tests for the legacy "peeking" clamshell launcher. These can be deleted when
-// ProductivityLauncher is the default.
-class AppListViewPeekingTest : public AppListViewTest {
- public:
-  AppListViewPeekingTest() {
-    feature_list_.InitAndDisableFeature(features::kProductivityLauncher);
-  }
-
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // Tests app list view layout for different screen sizes.
 class AppListViewScalableLayoutTest : public AppListViewTest {
  public:
@@ -881,17 +870,6 @@
 
 INSTANTIATE_TEST_SUITE_P(Rtl, AppListViewFocusTest, testing::Bool());
 
-// Tests for the legacy "peeking" clamshell launcher. These can be deleted when
-// ProductivityLauncher is the default.
-class AppListViewPeekingFocusTest : public AppListViewFocusTest {
- public:
-  AppListViewPeekingFocusTest() {
-    feature_list_.InitAndDisableFeature(features::kProductivityLauncher);
-  }
-
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // Tests that the initial focus is on search box.
 TEST_F(AppListViewFocusTest, InitialFocus) {
   EXPECT_EQ(search_box_view()->search_box(), focused_view());
@@ -1318,15 +1296,6 @@
   EXPECT_EQ(folder_item_view(), focused_view());
 }
 
-// Tests that opening the app list opens in fullscreen mode by default.
-TEST_F(AppListViewPeekingTest, ShowFullscreenByDefault) {
-  Initialize(false /*is_tablet_mode*/);
-
-  Show();
-
-  ASSERT_EQ(ash::AppListViewState::kFullscreenAllApps, view_->app_list_state());
-}
-
 // Tests that in tablet mode, the app list opens in fullscreen by default.
 TEST_F(AppListViewTest, ShowFullscreenWhenInTabletMode) {
   Initialize(/*is_tablet_mode=*/true);
@@ -1336,23 +1305,12 @@
   ASSERT_EQ(ash::AppListViewState::kFullscreenAllApps, view_->app_list_state());
 }
 
-// Tests that setting empty text in the search box does not change the state.
-TEST_F(AppListViewPeekingTest, EmptySearchTextStaysAtFullscreenAllApps) {
-  Initialize(false /*is_tablet_mode*/);
-  views::Textfield* search_box =
-      view_->app_list_main_view()->search_box_view()->search_box();
-
-  Show();
-  search_box->SetText(std::u16string());
-
-  ASSERT_EQ(ash::AppListViewState::kFullscreenAllApps, view_->app_list_state());
-}
-
 // Tests that typing when in fullscreen changes the state to fullscreen search.
-TEST_F(AppListViewPeekingTest, TypingFullscreenToFullscreenSearch) {
-  Initialize(false /*is_tablet_mode*/);
+TEST_F(AppListViewTest, TypingFullscreenToFullscreenSearch) {
+  Initialize(true /*is_tablet_mode*/);
   Show();
 
+  view_->SetState(AppListViewState::kFullscreenAllApps);
   views::Textfield* search_box =
       view_->app_list_main_view()->search_box_view()->search_box();
 
@@ -1378,17 +1336,6 @@
   EXPECT_EQ(ash::AppListViewState::kFullscreenSearch, view_->app_list_state());
 }
 
-// Tests that pressing escape when in fullscreen side-shelf closes the app list.
-TEST_F(AppListViewPeekingTest, EscapeKeySideShelfFullscreenToClosed) {
-  // Put into fullscreen by using side-shelf.
-  Initialize(false /*is_tablet_mode*/);
-
-  Show(/*is_side_shelf=*/true);
-  view_->AcceleratorPressed(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-
-  ASSERT_EQ(1, delegate_->dismiss_count());
-}
-
 // Tests that pressing escape when in tablet mode keeps app list in fullscreen.
 TEST_F(AppListViewTest, EscapeKeyTabletModeStayFullscreen) {
   // Put into fullscreen by using tablet mode.
@@ -1445,27 +1392,6 @@
   ASSERT_EQ(ash::AppListViewState::kFullscreenAllApps, view_->app_list_state());
 }
 
-// Tests that opening in fullscreen mode sets the correct height.
-TEST_F(AppListViewPeekingTest, OpenInFullscreenCorrectHeight) {
-  Initialize(false /*is_tablet_mode*/);
-
-  Show();
-  const int y = view_->GetWidget()->GetWindowBoundsInScreen().y();
-  ASSERT_EQ(0, y);
-}
-
-// Tests that AppListView::SetState succeeds when the state has been set to
-// CLOSED.
-TEST_F(AppListViewPeekingTest, SetStateFailsWhenClosing) {
-  Initialize(false /*is_tablet_mode*/);
-  Show();
-  view_->SetState(ash::AppListViewState::kClosed);
-
-  view_->SetState(ash::AppListViewState::kFullscreenAllApps);
-
-  ASSERT_EQ(ash::AppListViewState::kFullscreenAllApps, view_->app_list_state());
-}
-
 TEST_F(AppListViewTest, AppsGridViewVisibilityOnReopening) {
   Initialize(/*is_tablet_mode=*/true);
   Show();
@@ -1482,30 +1408,6 @@
 
 // Tests displaying the app list and performs a standard set of checks on its
 // top level views.
-TEST_F(AppListViewPeekingTest, DisplayTest) {
-  Initialize(/*is_tablet_mode=*/false);
-  EXPECT_EQ(-1, GetPaginationModel()->total_pages());
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-
-  Show();
-
-  // |view_| bounds equal to the root window's size.
-  EXPECT_EQ("800x600", view_->bounds().size().ToString());
-
-  EXPECT_EQ(2, GetPaginationModel()->total_pages());
-  EXPECT_EQ(0, GetPaginationModel()->selected_page());
-
-  // Checks on the main view.
-  AppListMainView* main_view = view_->app_list_main_view();
-  EXPECT_NO_FATAL_FAILURE(CheckView(main_view));
-  EXPECT_NO_FATAL_FAILURE(CheckView(main_view->contents_view()));
-
-  ash::AppListState expected = ash::AppListState::kStateApps;
-  EXPECT_TRUE(main_view->contents_view()->IsStateActive(expected));
-  EXPECT_EQ(expected, delegate_->GetCurrentAppListPage());
-}
-
-// As above above, but tests tablet mode with and without ProductivityLauncher.
 TEST_F(AppListViewTest, DisplayTest) {
   Initialize(/*is_tablet_mode=*/true);
   EXPECT_EQ(-1, GetPaginationModel()->total_pages());
@@ -1668,33 +1570,6 @@
   EXPECT_TRUE(view_->GetWidget()->IsVisible());
 }
 
-// Tests that context menus are not shown between app icons in clamshell mode.
-TEST_F(AppListViewPeekingTest, DontShowContextMenuBetweenAppsInClamshellMode) {
-  Initialize(false /* disable tablet mode */);
-  delegate_->GetTestModel()->PopulateApps(kInitialItems);
-  Show();
-
-  // Tap between two apps in clamshell mode.
-  const gfx::Point middle = GetPointBetweenTwoApps();
-  ui::GestureEvent tap(middle.x(), middle.y(), 0, base::TimeTicks(),
-                       ui::GestureEventDetails(ui::ET_GESTURE_TWO_FINGER_TAP));
-  view_->OnGestureEvent(&tap);
-
-  // The wallpaper menu should not show.
-  EXPECT_EQ(0, show_wallpaper_context_menu_count());
-  EXPECT_TRUE(view_->GetWidget()->IsVisible());
-
-  // Right click between two apps in clamshell mode.
-  ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, middle, middle,
-                             ui::EventTimeForNow(), ui::EF_RIGHT_MOUSE_BUTTON,
-                             ui::EF_RIGHT_MOUSE_BUTTON);
-  view_->OnMouseEvent(&mouse_event);
-
-  // The wallpaper menu should not show.
-  EXPECT_EQ(0, show_wallpaper_context_menu_count());
-  EXPECT_TRUE(view_->GetWidget()->IsVisible());
-}
-
 // Tests the back action in home launcher.
 TEST_F(AppListViewTest, BackAction) {
   // Put into fullscreen using tablet mode.
diff --git a/ash/app_list/views/productivity_launcher_search_view_unittest.cc b/ash/app_list/views/productivity_launcher_search_view_unittest.cc
index d228644..6504c941 100644
--- a/ash/app_list/views/productivity_launcher_search_view_unittest.cc
+++ b/ash/app_list/views/productivity_launcher_search_view_unittest.cc
@@ -56,9 +56,7 @@
  public:
   ProductivityLauncherSearchViewTest()
       : AshTestBase((base::test::TaskEnvironment::TimeSource::MOCK_TIME)),
-        test_under_tablet_(GetParam()) {
-    scoped_feature_list_.InitAndEnableFeature(features::kProductivityLauncher);
-  }
+        test_under_tablet_(GetParam()) {}
   ProductivityLauncherSearchViewTest(
       const ProductivityLauncherSearchViewTest&) = delete;
   ProductivityLauncherSearchViewTest& operator=(
@@ -164,7 +162,6 @@
 
  private:
   const bool test_under_tablet_;
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // An extension of ProductivityLauncherSearchViewTest to test launcher image
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index 7c83925..0b228b3 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -966,14 +966,6 @@
 const char kUsbPeripheralCableSpeedNotificationShown[] =
     "ash.usb_peripheral_cable_speed_notification_shown";
 
-// An integer pref that specifies how many times the Suggested Content privacy
-// info has been shown in Launcher. This value will increment by one every time
-// when Launcher changes state from Peeking to Half or FullscreenSearch up to a
-// predefined threshold, e.g. six times. If the info has been shown for more
-// than the threshold, do not show the privacy info any more.
-const char kSuggestedContentInfoShownInLauncher[] =
-    "ash.launcher.suggested_content_info_shown";
-
 // A dictionary value that determines whether the reorder nudge in app list
 // should show to the users.
 const char kAppListReorderNudge[] = "ash.launcher.app_list_reorder_nudge";
@@ -983,14 +975,6 @@
 const char kLauncherFilesPrivacyNotice[] =
     "ash.launcher.continue_section_privacy_notice";
 
-// A boolean pref that indicates whether the Suggested Content privacy info may
-// be displayed to user. A false value indicates that the info can be displayed
-// if the value of |kSuggestedContentInfoShownInLauncher| is smaller than the
-// predefined threshold. A true value implies that the user has dismissed the
-// info view, and do not show the privacy info any more.
-const char kSuggestedContentInfoDismissedInLauncher[] =
-    "ash.launcher.suggested_content_info_dismissed";
-
 // A boolean pref that indicates whether lock screen media controls are enabled.
 // Controlled by user policy.
 const char kLockScreenMediaControlsEnabled[] =
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index dc27f7b..72b3c8b5 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -432,10 +432,6 @@
 extern const char kUsbPeripheralCableSpeedNotificationShown[];
 
 COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const char kSuggestedContentInfoShownInLauncher[];
-COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const char kSuggestedContentInfoDismissedInLauncher[];
-COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kAppListReorderNudge[];
 
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/display/output_protection_delegate.cc b/ash/display/output_protection_delegate.cc
index 2ad374d8..eab160b 100644
--- a/ash/display/output_protection_delegate.cc
+++ b/ash/display/output_protection_delegate.cc
@@ -23,6 +23,12 @@
 
 void MaybeSetCaptureModeWindowProtection(aura::Window* window,
                                          uint32_t protection_mask) {
+  // `OutputProtectionDelegate` is not owned by ash. It is created by
+  // `OutputProtectionImpl` which exists in Chrome, and can invoke the delegate
+  // even after `Shell` has been destroyed. See b/256706119.
+  if (!Shell::HasInstance())
+    return;
+
   CaptureModeController::Get()->SetWindowProtectionMask(window,
                                                         protection_mask);
 }
diff --git a/ash/fast_ink/cursor/cursor_view.cc b/ash/fast_ink/cursor/cursor_view.cc
index 8755bec..afdadaf 100644
--- a/ash/fast_ink/cursor/cursor_view.cc
+++ b/ash/fast_ink/cursor/cursor_view.cc
@@ -82,9 +82,8 @@
 
   // Create transform used to convert cursor controller coordinates to screen
   // coordinates.
-  bool inversible = host()->window_to_buffer_transform().GetInverse(
-      &buffer_to_screen_transform_);
-  DCHECK(inversible);
+  buffer_to_screen_transform_ =
+      host()->window_to_buffer_transform().GetCheckedInverse();
 
   ui::CursorController::GetInstance()->AddCursorObserver(this);
 }
diff --git a/ash/fast_ink/fast_ink_host.cc b/ash/fast_ink/fast_ink_host.cc
index d2a050f..8e72fc7fd 100644
--- a/ash/fast_ink/fast_ink_host.cc
+++ b/ash/fast_ink/fast_ink_host.cc
@@ -391,9 +391,8 @@
   target_to_buffer_transform.Scale(1.f / device_scale_factor,
                                    1.f / device_scale_factor);
 
-  gfx::Transform buffer_to_target_transform;
-  bool rv = target_to_buffer_transform.GetInverse(&buffer_to_target_transform);
-  DCHECK(rv);
+  gfx::Transform buffer_to_target_transform =
+      target_to_buffer_transform.GetCheckedInverse();
 
   const viz::CompositorRenderPassId kRenderPassId{1};
   auto render_pass = viz::CompositorRenderPass::Create();
diff --git a/ash/fast_ink/view_tree_host_root_view.cc b/ash/fast_ink/view_tree_host_root_view.cc
index 5617ab29..e993fba 100644
--- a/ash/fast_ink/view_tree_host_root_view.cc
+++ b/ash/fast_ink/view_tree_host_root_view.cc
@@ -449,9 +449,8 @@
           buffer_size_, viz::RGBA_8888, is_overlay_candidate_);
   transferable_resource.id = id_generator_.GenerateNextId();
 
-  gfx::Transform buffer_to_target_transform;
-  bool rv = rotate_transform_.GetInverse(&buffer_to_target_transform);
-  DCHECK(rv);
+  gfx::Transform buffer_to_target_transform =
+      rotate_transform_.GetCheckedInverse();
 
   const viz::CompositorRenderPassId kRenderPassId{1};
   auto render_pass = viz::CompositorRenderPass::Create();
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc
index 00dedf9..e12fa5b 100644
--- a/ash/frame/non_client_frame_view_ash.cc
+++ b/ash/frame/non_client_frame_view_ash.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
-#include "ash/frame/header_view.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
@@ -24,6 +23,7 @@
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "chromeos/ui/frame/default_frame_header.h"
 #include "chromeos/ui/frame/frame_utils.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -157,7 +157,7 @@
                                            public views::ViewTargeterDelegate {
  public:
   METADATA_HEADER(OverlayView);
-  explicit OverlayView(HeaderView* header_view);
+  explicit OverlayView(chromeos::HeaderView* header_view);
   OverlayView(const OverlayView&) = delete;
   OverlayView& operator=(const OverlayView&) = delete;
   ~OverlayView() override;
@@ -170,10 +170,11 @@
   bool DoesIntersectRect(const views::View* target,
                          const gfx::Rect& rect) const override;
 
-  HeaderView* header_view_;
+  chromeos::HeaderView* header_view_;
 };
 
-NonClientFrameViewAsh::OverlayView::OverlayView(HeaderView* header_view)
+NonClientFrameViewAsh::OverlayView::OverlayView(
+    chromeos::HeaderView* header_view)
     : header_view_(header_view) {
   AddChildView(header_view);
   SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
@@ -213,7 +214,7 @@
 
 NonClientFrameViewAsh::NonClientFrameViewAsh(views::Widget* frame)
     : frame_(frame),
-      header_view_(new HeaderView(frame, this)),
+      header_view_(new chromeos::HeaderView(frame, this)),
       overlay_view_(new OverlayView(header_view_)),
       frame_context_menu_controller_(
           std::make_unique<FrameContextMenuController>(frame, this)) {
@@ -279,7 +280,7 @@
   header_view_->UpdateCaptionButtons();
 }
 
-HeaderView* NonClientFrameViewAsh::GetHeaderView() {
+chromeos::HeaderView* NonClientFrameViewAsh::GetHeaderView() {
   return header_view_;
 }
 
diff --git a/ash/frame/non_client_frame_view_ash.h b/ash/frame/non_client_frame_view_ash.h
index ffd28ca..b307a15 100644
--- a/ash/frame/non_client_frame_view_ash.h
+++ b/ash/frame/non_client_frame_view_ash.h
@@ -9,10 +9,10 @@
 
 #include "ash/ash_export.h"
 #include "ash/frame/frame_context_menu_controller.h"
-#include "ash/frame/header_view.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/highlight_border_overlay.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -71,7 +71,7 @@
   void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color);
 
   // Get the view of the header.
-  HeaderView* GetHeaderView();
+  chromeos::HeaderView* GetHeaderView();
 
   // Calculate the client bounds for given window bounds.
   gfx::Rect GetClientBoundsForWindowBounds(
@@ -160,7 +160,7 @@
   views::Widget* const frame_;
 
   // View which contains the title and window controls.
-  HeaderView* header_view_ = nullptr;
+  chromeos::HeaderView* header_view_ = nullptr;
 
   OverlayView* overlay_view_ = nullptr;
 
diff --git a/ash/frame/non_client_frame_view_ash_unittest.cc b/ash/frame/non_client_frame_view_ash_unittest.cc
index 3e0d7add..d984175 100644
--- a/ash/frame/non_client_frame_view_ash_unittest.cc
+++ b/ash/frame/non_client_frame_view_ash_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/constants/ash_pref_names.h"
-#include "ash/frame/header_view.h"
 #include "ash/frame/wide_frame_view.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
@@ -32,6 +31,7 @@
 #include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "chromeos/ui/frame/default_frame_header.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
@@ -97,7 +97,7 @@
     return non_client_frame_view_;
   }
 
-  HeaderView* header_view() const {
+  chromeos::HeaderView* header_view() const {
     return non_client_frame_view_->header_view_;
   }
 
@@ -416,7 +416,7 @@
   std::unique_ptr<views::Widget> widget = CreateTestWidget(delegate);
   NonClientFrameViewAsh* non_client_frame_view =
       delegate->non_client_frame_view();
-  HeaderView* header_view = non_client_frame_view->GetHeaderView();
+  chromeos::HeaderView* header_view = non_client_frame_view->GetHeaderView();
   EXPECT_FALSE(header_view->in_immersive_mode());
   EXPECT_TRUE(header_view->GetVisible());
   widget->SetFullscreen(true);
@@ -510,7 +510,7 @@
       delegate->non_client_frame_view();
   non_client_frame_view->SetCaptionButtonModel(std::move(model));
 
-  HeaderView* header_view = non_client_frame_view->GetHeaderView();
+  chromeos::HeaderView* header_view = non_client_frame_view->GetHeaderView();
   EXPECT_FALSE(header_view->GetBackButton());
   model_ptr->SetVisible(views::CAPTION_BUTTON_ICON_BACK, true);
   non_client_frame_view->SizeConstraintsChanged();
@@ -593,7 +593,7 @@
       delegate->non_client_frame_view();
   non_client_frame_view->SetCaptionButtonModel(std::move(model));
 
-  HeaderView* header_view = non_client_frame_view->GetHeaderView();
+  chromeos::HeaderView* header_view = non_client_frame_view->GetHeaderView();
   FrameCaptionButtonContainerView::TestApi test_api(
       header_view->caption_button_container());
 
@@ -680,14 +680,14 @@
 
   NonClientFrameViewAsh* non_client_frame_view =
       delegate->non_client_frame_view();
-  HeaderView* header_view = non_client_frame_view->GetHeaderView();
+  chromeos::HeaderView* header_view = non_client_frame_view->GetHeaderView();
   widget->Maximize();
 
   std::unique_ptr<WideFrameView> wide_frame_view =
       std::make_unique<WideFrameView>(widget.get());
   wide_frame_view->GetWidget()->Show();
 
-  HeaderView* wide_header_view = wide_frame_view->header_view();
+  chromeos::HeaderView* wide_header_view = wide_frame_view->header_view();
   display::Screen* screen = display::Screen::GetScreen();
 
   const gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
@@ -760,7 +760,7 @@
   std::unique_ptr<WideFrameView> wide_frame_view =
       std::make_unique<WideFrameView>(widget.get());
   wide_frame_view->GetWidget()->Show();
-  HeaderView* header_view = wide_frame_view->header_view();
+  chromeos::HeaderView* header_view = wide_frame_view->header_view();
   FrameCaptionButtonContainerView::TestApi test_api(
       header_view->caption_button_container());
 
@@ -952,7 +952,7 @@
 
   std::unique_ptr<WideFrameView> wide_frame_view =
       std::make_unique<WideFrameView>(widget.get());
-  HeaderView* wide_header_view = wide_frame_view->header_view();
+  chromeos::HeaderView* wide_header_view = wide_frame_view->header_view();
   DefaultFrameHeader* header = wide_header_view->GetFrameHeader();
   EXPECT_EQ(new_active_color, header->active_frame_color_for_testing());
   EXPECT_EQ(new_inactive_color, header->inactive_frame_color_for_testing());
diff --git a/ash/frame/wide_frame_view.cc b/ash/frame/wide_frame_view.cc
index 659c2eb..86a1b85 100644
--- a/ash/frame/wide_frame_view.cc
+++ b/ash/frame/wide_frame_view.cc
@@ -5,7 +5,6 @@
 #include "ash/frame/wide_frame_view.h"
 #include <memory>
 
-#include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/screen_util.h"
@@ -17,6 +16,7 @@
 #include "base/metrics/user_metrics.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "chromeos/ui/frame/default_frame_header.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
@@ -32,7 +32,7 @@
 
 class WideFrameTargeter : public aura::WindowTargeter {
  public:
-  explicit WideFrameTargeter(HeaderView* header_view)
+  explicit WideFrameTargeter(chromeos::HeaderView* header_view)
       : header_view_(header_view) {}
 
   WideFrameTargeter(const WideFrameTargeter&) = delete;
@@ -59,7 +59,7 @@
   }
 
  private:
-  HeaderView* header_view_;
+  chromeos::HeaderView* header_view_;
 };
 
 }  // namespace
@@ -102,7 +102,7 @@
   // Use the HeaderView itself as a frame view because WideFrameView is
   // is the frame only.
   header_view_ = AddChildView(
-      std::make_unique<HeaderView>(target, /*frame view=*/nullptr));
+      std::make_unique<chromeos::HeaderView>(target, /*frame view=*/nullptr));
   header_view_->Init();
   GetTargetHeaderView()->SetShouldPaintHeader(false);
   header_view_->set_context_menu_controller(
@@ -143,7 +143,7 @@
   if (widget_)
     widget_->CloseNow();
   if (target_) {
-    HeaderView* target_header_view = GetTargetHeaderView();
+    chromeos::HeaderView* target_header_view = GetTargetHeaderView();
     target_header_view->SetShouldPaintHeader(true);
     target_header_view->GetFrameHeader()->UpdateFrameHeaderKey();
     target_->GetNativeWindow()->RemoveObserver(this);
@@ -248,7 +248,7 @@
       gfx::Rect(point_in_header_coords, gfx::Size(1, 1)));
 }
 
-HeaderView* WideFrameView::GetTargetHeaderView() {
+chromeos::HeaderView* WideFrameView::GetTargetHeaderView() {
   auto* frame_view = static_cast<NonClientFrameViewAsh*>(
       target_->non_client_view()->frame_view());
   return frame_view->GetHeaderView();
diff --git a/ash/frame/wide_frame_view.h b/ash/frame/wide_frame_view.h
index ddefe88a..3daa320 100644
--- a/ash/frame/wide_frame_view.h
+++ b/ash/frame/wide_frame_view.h
@@ -16,6 +16,7 @@
 
 namespace chromeos {
 class ImmersiveFullscreenController;
+class HeaderView;
 }
 
 namespace views {
@@ -23,7 +24,6 @@
 }
 
 namespace ash {
-class HeaderView;
 
 // WideFrameView is used for the case where the widget's maximzed/fullscreen
 // doesn't cover the entire workarea/display area but the caption frame should
@@ -57,7 +57,7 @@
   void SetCaptionButtonModel(
       std::unique_ptr<chromeos::CaptionButtonModel> mode);
 
-  HeaderView* header_view() { return header_view_; }
+  chromeos::HeaderView* header_view() { return header_view_; }
 
  private:
   static gfx::Rect GetFrameBounds(views::Widget* target);
@@ -85,7 +85,7 @@
   bool ShouldShowContextMenu(views::View* source,
                              const gfx::Point& screen_coords_point) override;
 
-  HeaderView* GetTargetHeaderView();
+  chromeos::HeaderView* GetTargetHeaderView();
 
   // The target widget this frame will control.
   views::Widget* target_;
@@ -94,7 +94,7 @@
 
   display::ScopedDisplayObserver display_observer_{this};
 
-  HeaderView* header_view_ = nullptr;
+  chromeos::HeaderView* header_view_ = nullptr;
 
   std::unique_ptr<FrameContextMenuController> frame_context_menu_controller_;
 
diff --git a/ash/ime/ime_controller_impl.cc b/ash/ime/ime_controller_impl.cc
index 0a21cb8..f092374 100644
--- a/ash/ime/ime_controller_impl.cc
+++ b/ash/ime/ime_controller_impl.cc
@@ -301,7 +301,7 @@
 }
 
 void ImeControllerImpl::OnScreenCaptureStart(
-    const base::RepeatingClosure& stop_callback,
+    base::OnceClosure stop_callback,
     const base::RepeatingClosure& source_callback,
     const std::u16string& screen_capture_status) {
   client_->UpdateCastingState(true);
diff --git a/ash/ime/ime_controller_impl.h b/ash/ime/ime_controller_impl.h
index c2734ed2..2f9c80e 100644
--- a/ash/ime/ime_controller_impl.h
+++ b/ash/ime/ime_controller_impl.h
@@ -127,7 +127,7 @@
 
   // ScreenCaptureObserver:
   void OnScreenCaptureStart(
-      const base::RepeatingClosure& stop_callback,
+      base::OnceClosure stop_callback,
       const base::RepeatingClosure& source_callback,
       const std::u16string& screen_capture_status) override;
   void OnScreenCaptureStop() override;
diff --git a/ash/public/cpp/app_list/app_list_types.cc b/ash/public/cpp/app_list/app_list_types.cc
index 31caf2d..8c659ecb 100644
--- a/ash/public/cpp/app_list/app_list_types.cc
+++ b/ash/public/cpp/app_list/app_list_types.cc
@@ -23,6 +23,7 @@
     case AppListSearchResultType::kArcAppShortcut:
     case AppListSearchResultType::kInstantApp:
     case AppListSearchResultType::kGames:
+    case AppListSearchResultType::kZeroStateApp:
       return true;
     case AppListSearchResultType::kUnknown:
     case AppListSearchResultType::kOmnibox:
@@ -44,11 +45,12 @@
   }
 }
 
-bool IsContinueSectionResultType(AppListSearchResultType result_type) {
+bool IsZeroStateResultType(AppListSearchResultType result_type) {
   switch (result_type) {
     case AppListSearchResultType::kZeroStateFile:
     case AppListSearchResultType::kZeroStateDrive:
     case AppListSearchResultType::kZeroStateHelpApp:
+    case AppListSearchResultType::kZeroStateApp:
       return true;
     case AppListSearchResultType::kUnknown:
     case AppListSearchResultType::kInstalledApp:
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index df3dc9bb..eec7192 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -375,16 +375,18 @@
   kGames,                  // Game sarch results.
   kPersonalization,        // Personalization search results.
   kZeroStateHelpApp,       // Help App (aka Explore) results for zero-state.
+  kZeroStateApp,           // App recommendations for zero-state / recent apps.
   // Add new values here.
-  kMaxValue = kZeroStateHelpApp,
+  kMaxValue = kZeroStateApp,
 };
 
 ASH_PUBLIC_EXPORT bool IsAppListSearchResultAnApp(
     AppListSearchResultType result_type);
 
 // Returns whether the result type is a type of result shown in launcher
-// continue section.
-ASH_PUBLIC_EXPORT bool IsContinueSectionResultType(
+// apps page, i.e. results shown in launcher "continue" section and among recent
+// apps.
+ASH_PUBLIC_EXPORT bool IsZeroStateResultType(
     AppListSearchResultType result_type);
 
 // The different categories a search result can be part of. Every search result
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.cc b/ash/quick_pair/repository/fast_pair_repository_impl.cc
index 2247d3e9..3a886ee4 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.cc
@@ -238,7 +238,7 @@
       const std::string& string_key = info.device().account_key();
       const std::vector<uint8_t> binary_key(string_key.begin(),
                                             string_key.end());
-      if (account_key_filter.Test(binary_key)) {
+      if (account_key_filter.IsAccountKeyInFilter(binary_key)) {
         nearby::fastpair::StoredDiscoveryItem device;
         if (device.ParseFromString(info.device().discovery_item_bytes())) {
           QP_LOG(INFO) << "Account key matched with a paired device: "
diff --git a/ash/system/accessibility/accessibility_detailed_view.cc b/ash/system/accessibility/accessibility_detailed_view.cc
index 816e106..f54f504 100644
--- a/ash/system/accessibility/accessibility_detailed_view.cc
+++ b/ash/system/accessibility/accessibility_detailed_view.cc
@@ -110,6 +110,17 @@
          language_code == GetSodaFeatureLocale(feature);
 }
 
+// Updates the check mark to `checked` on `view1` and `view2` if the views
+// exist.
+void UpdateCheckMark(bool checked,
+                     HoverHighlightView* view1,
+                     HoverHighlightView* view2) {
+  if (view1)
+    TrayPopupUtils::UpdateCheckMarkVisibility(view1, checked);
+  if (view2)
+    TrayPopupUtils::UpdateCheckMarkVisibility(view2, checked);
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -152,94 +163,89 @@
   AccessibilityControllerImpl* controller =
       Shell::Get()->accessibility_controller();
 
-  if (spoken_feedback_view_ &&
-      controller->IsSpokenFeedbackSettingVisibleInTray()) {
+  if (controller->IsSpokenFeedbackSettingVisibleInTray()) {
     bool checked = controller->spoken_feedback().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(spoken_feedback_view_, checked);
+    UpdateCheckMark(checked, spoken_feedback_view_, spoken_feedback_top_view_);
   }
 
-  if (select_to_speak_view_ &&
-      controller->IsSelectToSpeakSettingVisibleInTray()) {
+  if (controller->IsSelectToSpeakSettingVisibleInTray()) {
     bool checked = controller->select_to_speak().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(select_to_speak_view_, checked);
+    UpdateCheckMark(checked, select_to_speak_view_, select_to_speak_top_view_);
   }
 
-  if (dictation_view_ && controller->IsDictationSettingVisibleInTray()) {
+  if (controller->IsDictationSettingVisibleInTray()) {
     bool checked = controller->dictation().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(dictation_view_, checked);
+    UpdateCheckMark(checked, dictation_view_, dictation_top_view_);
   }
 
-  if (high_contrast_view_ && controller->IsHighContrastSettingVisibleInTray()) {
+  if (controller->IsHighContrastSettingVisibleInTray()) {
     bool checked = controller->high_contrast().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(high_contrast_view_, checked);
+    UpdateCheckMark(checked, high_contrast_view_, high_contrast_top_view_);
   }
 
-  if (screen_magnifier_view_ &&
-      controller->IsFullScreenMagnifierSettingVisibleInTray()) {
+  if (controller->IsFullScreenMagnifierSettingVisibleInTray()) {
     bool checked = delegate->IsMagnifierEnabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(screen_magnifier_view_, checked);
+    UpdateCheckMark(checked, screen_magnifier_view_,
+                    screen_magnifier_top_view_);
   }
 
-  if (docked_magnifier_view_ &&
-      controller->IsDockedMagnifierSettingVisibleInTray()) {
+  if (controller->IsDockedMagnifierSettingVisibleInTray()) {
     bool checked = Shell::Get()->docked_magnifier_controller()->GetEnabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(docked_magnifier_view_, checked);
+    UpdateCheckMark(checked, docked_magnifier_view_,
+                    docked_magnifier_top_view_);
   }
 
-  if (autoclick_view_ && controller->IsAutoclickSettingVisibleInTray()) {
+  if (controller->IsAutoclickSettingVisibleInTray()) {
     bool checked = controller->autoclick().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(autoclick_view_, checked);
+    UpdateCheckMark(checked, autoclick_view_, autoclick_top_view_);
   }
 
-  if (virtual_keyboard_view_ &&
-      controller->IsVirtualKeyboardSettingVisibleInTray()) {
+  if (controller->IsVirtualKeyboardSettingVisibleInTray()) {
     bool checked = controller->virtual_keyboard().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(virtual_keyboard_view_, checked);
+    UpdateCheckMark(checked, virtual_keyboard_view_,
+                    virtual_keyboard_top_view_);
   }
 
-  if (switch_access_view_ && controller->IsSwitchAccessSettingVisibleInTray()) {
+  if (controller->IsSwitchAccessSettingVisibleInTray()) {
     bool checked = controller->switch_access().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(switch_access_view_, checked);
+    UpdateCheckMark(checked, switch_access_view_, switch_access_top_view_);
   }
 
-  if (live_caption_view_ && controller->IsLiveCaptionSettingVisibleInTray()) {
+  if (controller->IsLiveCaptionSettingVisibleInTray()) {
     bool checked = controller->live_caption().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(live_caption_view_, checked);
+    UpdateCheckMark(checked, live_caption_view_, live_caption_top_view_);
   }
 
-  if (large_cursor_view_ && controller->IsLargeCursorSettingVisibleInTray()) {
+  if (controller->IsLargeCursorSettingVisibleInTray()) {
     bool checked = controller->large_cursor().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(large_cursor_view_, checked);
+    UpdateCheckMark(checked, large_cursor_view_, large_cursor_top_view_);
   }
 
-  if (mono_audio_view_ && controller->IsMonoAudioSettingVisibleInTray()) {
+  if (controller->IsMonoAudioSettingVisibleInTray()) {
     bool checked = controller->mono_audio().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(mono_audio_view_, checked);
+    UpdateCheckMark(checked, mono_audio_view_, mono_audio_top_view_);
   }
 
-  if (caret_highlight_view_ &&
-      controller->IsCaretHighlightSettingVisibleInTray()) {
+  if (controller->IsCaretHighlightSettingVisibleInTray()) {
     bool checked = controller->caret_highlight().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(caret_highlight_view_, checked);
+    UpdateCheckMark(checked, caret_highlight_view_, caret_highlight_top_view_);
   }
 
-  if (highlight_mouse_cursor_view_ &&
-      controller->IsCursorHighlightSettingVisibleInTray()) {
+  if (controller->IsCursorHighlightSettingVisibleInTray()) {
     bool checked = controller->cursor_highlight().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(highlight_mouse_cursor_view_,
-                                              checked);
+    UpdateCheckMark(checked, highlight_mouse_cursor_view_,
+                    highlight_mouse_cursor_top_view_);
   }
 
-  if (highlight_keyboard_focus_view_ &&
-      controller->IsFocusHighlightSettingVisibleInTray()) {
+  if (controller->IsFocusHighlightSettingVisibleInTray()) {
     bool checked = controller->focus_highlight().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(highlight_keyboard_focus_view_,
-                                              checked);
+    UpdateCheckMark(checked, highlight_keyboard_focus_view_,
+                    highlight_keyboard_focus_top_view_);
   }
 
-  if (sticky_keys_view_ && controller->IsStickyKeysSettingVisibleInTray()) {
+  if (controller->IsStickyKeysSettingVisibleInTray()) {
     bool checked = controller->sticky_keys().enabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(sticky_keys_view_, checked);
+    UpdateCheckMark(checked, sticky_keys_view_, sticky_keys_top_view_);
   }
 }
 
@@ -262,8 +268,82 @@
 }
 
 void AccessibilityDetailedView::AddEnabledFeatures(views::View* container) {
-  // TODO(b/252871844): Implement by creating a HoverHighlightView instance for
-  // each enabled feature, using the Add*View() helper methods.
+  AccessibilityControllerImpl* controller =
+      Shell::Get()->accessibility_controller();
+
+  if (controller->IsSpokenFeedbackSettingVisibleInTray() &&
+      controller->spoken_feedback().enabled()) {
+    spoken_feedback_top_view_ = AddSpokenFeedbackView(container);
+  }
+  if (controller->IsSelectToSpeakSettingVisibleInTray() &&
+      controller->select_to_speak().enabled()) {
+    select_to_speak_top_view_ = AddSelectToSpeakView(container);
+  }
+  if (controller->IsDictationSettingVisibleInTray() &&
+      controller->dictation().enabled()) {
+    dictation_top_view_ = AddDictationView(container);
+  }
+  if (controller->IsHighContrastSettingVisibleInTray() &&
+      controller->high_contrast().enabled()) {
+    high_contrast_top_view_ = AddHighContrastView(container);
+  }
+  if (controller->IsFullScreenMagnifierSettingVisibleInTray() &&
+      Shell::Get()->accessibility_delegate()->IsMagnifierEnabled()) {
+    screen_magnifier_top_view_ = AddScreenMagnifierView(container);
+  }
+  if (controller->IsDockedMagnifierSettingVisibleInTray() &&
+      Shell::Get()->docked_magnifier_controller()->GetEnabled()) {
+    docked_magnifier_top_view_ = AddDockedMagnifierView(container);
+  }
+  if (controller->IsAutoclickSettingVisibleInTray() &&
+      controller->autoclick().enabled()) {
+    autoclick_top_view_ = AddAutoclickView(container);
+  }
+  if (controller->IsVirtualKeyboardSettingVisibleInTray() &&
+      controller->virtual_keyboard().enabled()) {
+    virtual_keyboard_top_view_ = AddVirtualKeyboardView(container);
+  }
+  if (controller->IsSwitchAccessSettingVisibleInTray() &&
+      controller->switch_access().enabled()) {
+    switch_access_top_view_ = AddSwitchAccessView(container);
+  }
+  if (controller->IsLiveCaptionSettingVisibleInTray() &&
+      controller->live_caption().enabled()) {
+    live_caption_top_view_ = AddLiveCaptionView(container);
+  }
+  if (controller->IsLargeCursorSettingVisibleInTray() &&
+      controller->large_cursor().enabled()) {
+    large_cursor_top_view_ = AddLargeCursorView(container);
+  }
+  if (controller->IsMonoAudioSettingVisibleInTray() &&
+      controller->mono_audio().enabled()) {
+    mono_audio_top_view_ = AddMonoAudioView(container);
+  }
+  if (controller->IsCaretHighlightSettingVisibleInTray() &&
+      controller->caret_highlight().enabled()) {
+    caret_highlight_top_view_ = AddCaretHighlightView(container);
+  }
+  if (controller->IsCursorHighlightSettingVisibleInTray() &&
+      controller->cursor_highlight().enabled()) {
+    highlight_mouse_cursor_top_view_ = AddHighlightMouseCursorView(container);
+  }
+  // Focus highlighting can't be on when spoken feedback is on because
+  // ChromeVox does its own focus highlighting.
+  if (!controller->spoken_feedback().enabled() &&
+      controller->IsFocusHighlightSettingVisibleInTray() &&
+      controller->focus_highlight().enabled()) {
+    highlight_keyboard_focus_top_view_ =
+        AddHighlightKeyboardFocusView(container);
+  }
+  if (controller->IsStickyKeysSettingVisibleInTray() &&
+      controller->sticky_keys().enabled()) {
+    sticky_keys_top_view_ = AddStickyKeysView(container);
+  }
+
+  // Add a divider line if any features were added above.
+  if (!container->children().empty()) {
+    container->AddChildView(TrayPopupUtils::CreateListSubHeaderSeparator());
+  }
 }
 
 void AccessibilityDetailedView::AddAllFeatures(views::View* container) {
@@ -526,7 +606,10 @@
       Shell::Get()->accessibility_controller();
   using base::RecordAction;
   using base::UserMetricsAction;
-  if (spoken_feedback_view_ && view == spoken_feedback_view_ &&
+  // Since `view` is never null, there's no need to check for the existence of
+  // individual views in the if statements below.
+  DCHECK(view);
+  if ((view == spoken_feedback_top_view_ || view == spoken_feedback_view_) &&
       !controller->IsEnterpriseIconVisibleForSpokenFeedback()) {
     bool new_state = !controller->spoken_feedback().enabled();
     RecordAction(new_state
@@ -535,7 +618,8 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::SPOKEN_FEEDBACK,
                               new_state);
     controller->SetSpokenFeedbackEnabled(new_state, A11Y_NOTIFICATION_NONE);
-  } else if (select_to_speak_view_ && view == select_to_speak_view_ &&
+  } else if ((view == select_to_speak_top_view_ ||
+              view == select_to_speak_view_) &&
              !controller->IsEnterpriseIconVisibleForSelectToSpeak()) {
     bool new_state = !controller->select_to_speak().enabled();
     RecordAction(new_state
@@ -544,14 +628,14 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::SELECT_TO_SPEAK,
                               new_state);
     controller->select_to_speak().SetEnabled(new_state);
-  } else if (dictation_view_ && view == dictation_view_ &&
+  } else if ((view == dictation_top_view_ || view == dictation_view_) &&
              !controller->IsEnterpriseIconVisibleForDictation()) {
     bool new_state = !controller->dictation().enabled();
     RecordAction(new_state ? UserMetricsAction("StatusArea_DictationEnabled")
                            : UserMetricsAction("StatusArea_DictationDisabled"));
     LogUserAccessibilityEvent(UserSettingsEvent::Event::DICTATION, new_state);
     controller->dictation().SetEnabled(new_state);
-  } else if (high_contrast_view_ && view == high_contrast_view_ &&
+  } else if ((view == high_contrast_top_view_ || view == high_contrast_view_) &&
              !controller->IsEnterpriseIconVisibleForHighContrast()) {
     bool new_state = !controller->high_contrast().enabled();
     RecordAction(new_state
@@ -560,14 +644,16 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::HIGH_CONTRAST,
                               new_state);
     controller->high_contrast().SetEnabled(new_state);
-  } else if (screen_magnifier_view_ && view == screen_magnifier_view_ &&
+  } else if ((view == screen_magnifier_top_view_ ||
+              view == screen_magnifier_view_) &&
              !controller->IsEnterpriseIconVisibleForFullScreenMagnifier()) {
     bool new_state = !delegate->IsMagnifierEnabled();
     RecordAction(new_state ? UserMetricsAction("StatusArea_MagnifierEnabled")
                            : UserMetricsAction("StatusArea_MagnifierDisabled"));
     LogUserAccessibilityEvent(UserSettingsEvent::Event::MAGNIFIER, new_state);
     delegate->SetMagnifierEnabled(new_state);
-  } else if (docked_magnifier_view_ && view == docked_magnifier_view_ &&
+  } else if ((view == docked_magnifier_top_view_ ||
+              view == docked_magnifier_view_) &&
              !controller->IsEnterpriseIconVisibleForDockedMagnifier()) {
     auto* docked_magnifier_controller =
         Shell::Get()->docked_magnifier_controller();
@@ -585,7 +671,7 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::DOCKED_MAGNIFIER,
                               new_state);
     docked_magnifier_controller->SetEnabled(new_state);
-  } else if (large_cursor_view_ && view == large_cursor_view_ &&
+  } else if ((view == large_cursor_top_view_ || view == large_cursor_view_) &&
              !controller->IsEnterpriseIconVisibleForLargeCursor()) {
     bool new_state = !controller->large_cursor().enabled();
     RecordAction(new_state
@@ -594,14 +680,15 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::LARGE_CURSOR,
                               new_state);
     controller->large_cursor().SetEnabled(new_state);
-  } else if (autoclick_view_ && view == autoclick_view_ &&
+  } else if ((view == autoclick_top_view_ || view == autoclick_view_) &&
              !controller->IsEnterpriseIconVisibleForAutoclick()) {
     bool new_state = !controller->autoclick().enabled();
     RecordAction(new_state ? UserMetricsAction("StatusArea_AutoClickEnabled")
                            : UserMetricsAction("StatusArea_AutoClickDisabled"));
     LogUserAccessibilityEvent(UserSettingsEvent::Event::AUTO_CLICK, new_state);
     controller->autoclick().SetEnabled(new_state);
-  } else if (virtual_keyboard_view_ && view == virtual_keyboard_view_ &&
+  } else if ((view == virtual_keyboard_top_view_ ||
+              view == virtual_keyboard_view_) &&
              !controller->IsEnterpriseIconVisibleForVirtualKeyboard()) {
     bool new_state = !controller->virtual_keyboard().enabled();
     RecordAction(new_state
@@ -610,7 +697,7 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::VIRTUAL_KEYBOARD,
                               new_state);
     controller->virtual_keyboard().SetEnabled(new_state);
-  } else if (switch_access_view_ && view == switch_access_view_ &&
+  } else if ((view == switch_access_top_view_ || view == switch_access_view_) &&
              !controller->IsEnterpriseIconVisibleForSwitchAccess()) {
     bool new_state = !controller->switch_access().enabled();
     RecordAction(new_state
@@ -619,7 +706,7 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::SWITCH_ACCESS,
                               new_state);
     controller->switch_access().SetEnabled(new_state);
-  } else if (live_caption_view_ && view == live_caption_view_) {
+  } else if (view == live_caption_top_view_ || view == live_caption_view_) {
     bool new_state = !controller->live_caption().enabled();
     RecordAction(new_state
                      ? UserMetricsAction("StatusArea_LiveCaptionEnabled")
@@ -627,7 +714,8 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::LIVE_CAPTION,
                               new_state);
     controller->live_caption().SetEnabled(new_state);
-  } else if (caret_highlight_view_ && view == caret_highlight_view_ &&
+  } else if ((view == caret_highlight_top_view_ ||
+              view == caret_highlight_view_) &&
              !controller->IsEnterpriseIconVisibleForCaretHighlight()) {
     bool new_state = !controller->caret_highlight().enabled();
     RecordAction(new_state
@@ -636,15 +724,15 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::CARET_HIGHLIGHT,
                               new_state);
     controller->caret_highlight().SetEnabled(new_state);
-  } else if (mono_audio_view_ && view == mono_audio_view_ &&
+  } else if ((view == mono_audio_top_view_ || view == mono_audio_view_) &&
              !controller->IsEnterpriseIconVisibleForMonoAudio()) {
     bool new_state = !controller->mono_audio().enabled();
     RecordAction(new_state ? UserMetricsAction("StatusArea_MonoAudioEnabled")
                            : UserMetricsAction("StatusArea_MonoAudioDisabled"));
     LogUserAccessibilityEvent(UserSettingsEvent::Event::MONO_AUDIO, new_state);
     controller->mono_audio().SetEnabled(new_state);
-  } else if (highlight_mouse_cursor_view_ &&
-             view == highlight_mouse_cursor_view_ &&
+  } else if ((view == highlight_mouse_cursor_top_view_ ||
+              view == highlight_mouse_cursor_view_) &&
              !controller->IsEnterpriseIconVisibleForCursorHighlight()) {
     bool new_state = !controller->cursor_highlight().enabled();
     RecordAction(
@@ -654,8 +742,8 @@
     LogUserAccessibilityEvent(UserSettingsEvent::Event::HIGHLIGHT_MOUSE_CURSOR,
                               new_state);
     controller->cursor_highlight().SetEnabled(new_state);
-  } else if (highlight_keyboard_focus_view_ &&
-             view == highlight_keyboard_focus_view_ &&
+  } else if ((view == highlight_keyboard_focus_top_view_ ||
+              view == highlight_keyboard_focus_view_) &&
              !controller->IsEnterpriseIconVisibleForFocusHighlight()) {
     bool new_state = !controller->focus_highlight().enabled();
     RecordAction(
@@ -665,7 +753,7 @@
     LogUserAccessibilityEvent(
         UserSettingsEvent::Event::HIGHLIGHT_KEYBOARD_FOCUS, new_state);
     controller->focus_highlight().SetEnabled(new_state);
-  } else if (sticky_keys_view_ && view == sticky_keys_view_ &&
+  } else if ((view == sticky_keys_top_view_ || view == sticky_keys_view_) &&
              !controller->IsEnterpriseIconVisibleForStickyKeys()) {
     bool new_state = !controller->sticky_keys().enabled();
     RecordAction(new_state
diff --git a/ash/system/accessibility/accessibility_detailed_view.h b/ash/system/accessibility/accessibility_detailed_view.h
index 5239b86..7f72607 100644
--- a/ash/system/accessibility/accessibility_detailed_view.h
+++ b/ash/system/accessibility/accessibility_detailed_view.h
@@ -128,6 +128,26 @@
   HoverHighlightView* highlight_keyboard_focus_view_ = nullptr;
   HoverHighlightView* sticky_keys_view_ = nullptr;
 
+  // Views that appear in the top section listing enabled items. Created if the
+  // feature is enabled, otherwise nullptr. Owned by views hierarchy.
+  // Only used with QsRevamp.
+  HoverHighlightView* spoken_feedback_top_view_ = nullptr;
+  HoverHighlightView* select_to_speak_top_view_ = nullptr;
+  HoverHighlightView* dictation_top_view_ = nullptr;
+  HoverHighlightView* high_contrast_top_view_ = nullptr;
+  HoverHighlightView* screen_magnifier_top_view_ = nullptr;
+  HoverHighlightView* docked_magnifier_top_view_ = nullptr;
+  HoverHighlightView* large_cursor_top_view_ = nullptr;
+  HoverHighlightView* autoclick_top_view_ = nullptr;
+  HoverHighlightView* virtual_keyboard_top_view_ = nullptr;
+  HoverHighlightView* switch_access_top_view_ = nullptr;
+  HoverHighlightView* live_caption_top_view_ = nullptr;
+  HoverHighlightView* mono_audio_top_view_ = nullptr;
+  HoverHighlightView* caret_highlight_top_view_ = nullptr;
+  HoverHighlightView* highlight_mouse_cursor_top_view_ = nullptr;
+  HoverHighlightView* highlight_keyboard_focus_top_view_ = nullptr;
+  HoverHighlightView* sticky_keys_top_view_ = nullptr;
+
   views::Button* help_view_ = nullptr;
   views::Button* settings_view_ = nullptr;
 
diff --git a/ash/system/accessibility/accessibility_detailed_view_unittest.cc b/ash/system/accessibility/accessibility_detailed_view_unittest.cc
index 6bc6482..12286c2 100644
--- a/ash/system/accessibility/accessibility_detailed_view_unittest.cc
+++ b/ash/system/accessibility/accessibility_detailed_view_unittest.cc
@@ -118,12 +118,24 @@
   return speech::LanguageCode::kFrFr;
 }
 
+// Returns true if `view` is marked checked for accessibility.
+bool IsChecked(views::View* view) {
+  ui::AXNodeData node_data;
+  view->GetAccessibleNodeData(&node_data);
+  return node_data.GetCheckedState() == ax::mojom::CheckedState::kTrue;
+}
+
 }  // namespace
 
 class AccessibilityDetailedViewTest : public AshTestBase,
                                       public AccessibilityObserver {
  public:
-  AccessibilityDetailedViewTest() = default;
+  AccessibilityDetailedViewTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {media::kLiveCaption, media::kLiveCaptionSystemWideOnChromeOS,
+         ash::features::kOnDeviceSpeechRecognition},
+        {});
+  }
   AccessibilityDetailedViewTest(const AccessibilityDetailedViewTest&) = delete;
   AccessibilityDetailedViewTest& operator=(
       const AccessibilityDetailedViewTest&) = delete;
@@ -131,10 +143,6 @@
 
  protected:
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {media::kLiveCaption, media::kLiveCaptionSystemWideOnChromeOS,
-         ash::features::kOnDeviceSpeechRecognition},
-        {});
     AshTestBase::SetUp();
     controller_ = Shell::Get()->accessibility_controller();
     controller_->AddObserver(this);
@@ -310,11 +318,8 @@
   // enabled. Check that the checked state and detailed_menu_'s local state are
   // the same.
   bool IsEnabledOnDetailMenu(bool enabled_state, views::View* view) const {
-    ui::AXNodeData node_data;
-    view->GetAccessibleNodeData(&node_data);
-    bool checked_for_accessibility =
-        node_data.GetCheckedState() == ax::mojom::CheckedState::kTrue;
-    DCHECK(enabled_state == checked_for_accessibility);
+    bool checked_for_accessibility = IsChecked(view);
+    DCHECK_EQ(enabled_state, checked_for_accessibility);
     return enabled_state && checked_for_accessibility;
   }
 
@@ -410,6 +415,7 @@
     return detailed_menu_->GetClassName();
   }
 
+  AccessibilityControllerImpl* controller() { return controller_; }
   AccessibilityDetailedView* detailed_menu() { return detailed_menu_.get(); }
 
   // Accessors for list item views.
@@ -462,6 +468,56 @@
     return detailed_menu_->switch_access_view_;
   }
 
+  // Accessors for the top views listing enabled items.
+  HoverHighlightView* spoken_feedback_top_view() const {
+    return detailed_menu_->spoken_feedback_top_view_;
+  }
+  HoverHighlightView* select_to_speak_top_view() const {
+    return detailed_menu_->select_to_speak_top_view_;
+  }
+  HoverHighlightView* dictation_top_view() const {
+    return detailed_menu_->dictation_top_view_;
+  }
+  HoverHighlightView* high_contrast_top_view() const {
+    return detailed_menu_->high_contrast_top_view_;
+  }
+  HoverHighlightView* screen_magnifier_top_view() const {
+    return detailed_menu_->screen_magnifier_top_view_;
+  }
+  HoverHighlightView* docked_magnifier_top_view() const {
+    return detailed_menu_->docked_magnifier_top_view_;
+  }
+  HoverHighlightView* large_cursor_top_view() const {
+    return detailed_menu_->large_cursor_top_view_;
+  }
+  HoverHighlightView* live_caption_top_view() const {
+    return detailed_menu_->live_caption_top_view_;
+  }
+  HoverHighlightView* autoclick_top_view() const {
+    return detailed_menu_->autoclick_top_view_;
+  }
+  HoverHighlightView* virtual_keyboard_top_view() const {
+    return detailed_menu_->virtual_keyboard_top_view_;
+  }
+  HoverHighlightView* mono_audio_top_view() const {
+    return detailed_menu_->mono_audio_top_view_;
+  }
+  HoverHighlightView* caret_highlight_top_view() const {
+    return detailed_menu_->caret_highlight_top_view_;
+  }
+  HoverHighlightView* highlight_mouse_cursor_top_view() const {
+    return detailed_menu_->highlight_mouse_cursor_top_view_;
+  }
+  HoverHighlightView* highlight_keyboard_focus_top_view() const {
+    return detailed_menu_->highlight_keyboard_focus_top_view_;
+  }
+  HoverHighlightView* sticky_keys_top_view() const {
+    return detailed_menu_->sticky_keys_top_view_;
+  }
+  HoverHighlightView* switch_access_top_view() const {
+    return detailed_menu_->switch_access_top_view_;
+  }
+
  private:
   // AccessibilityObserver:
   void OnAccessibilityStatusChanged() override {
@@ -478,9 +534,18 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(AccessibilityDetailedViewTest, ListItemsAreInRoundedContainer) {
-  base::test::ScopedFeatureList feature_list{features::kQsRevamp};
+class AccessibilityDetailedViewQsRevampTest
+    : public AccessibilityDetailedViewTest {
+ public:
+  AccessibilityDetailedViewQsRevampTest() {
+    feature_list_.InitAndEnableFeature(features::kQsRevamp);
+  }
 
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, ListItemsAreInRoundedContainer) {
   CreateDetailedMenu();
   auto has_rounded_container_parent = [](views::View* view) -> bool {
     return views::IsViewClass<RoundedContainer>(view->parent());
@@ -504,6 +569,214 @@
   CloseDetailMenu();
 }
 
+TEST_F(AccessibilityDetailedViewQsRevampTest,
+       TopsViewsAreEmptyWithNoFeaturesEnabled) {
+  CreateDetailedMenu();
+
+  // By default none of the accessibility features are enabled, so none of the
+  // top-views are created.
+  EXPECT_FALSE(spoken_feedback_top_view());
+  EXPECT_FALSE(select_to_speak_top_view());
+  EXPECT_FALSE(dictation_top_view());
+  EXPECT_FALSE(high_contrast_top_view());
+  EXPECT_FALSE(screen_magnifier_top_view());
+  EXPECT_FALSE(docked_magnifier_top_view());
+  EXPECT_FALSE(large_cursor_top_view());
+  EXPECT_FALSE(live_caption_top_view());
+  EXPECT_FALSE(autoclick_top_view());
+  EXPECT_FALSE(virtual_keyboard_top_view());
+  EXPECT_FALSE(mono_audio_top_view());
+  EXPECT_FALSE(caret_highlight_top_view());
+  EXPECT_FALSE(highlight_mouse_cursor_top_view());
+  EXPECT_FALSE(highlight_keyboard_focus_top_view());
+  EXPECT_FALSE(sticky_keys_top_view());
+  EXPECT_FALSE(switch_access_top_view());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, SpokenFeedbackTopView) {
+  EnableSpokenFeedback(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(spoken_feedback_top_view());
+  EXPECT_TRUE(IsChecked(spoken_feedback_top_view()));
+
+  ClickView(spoken_feedback_top_view());
+  EXPECT_FALSE(IsChecked(spoken_feedback_top_view()));
+  EXPECT_FALSE(controller()->spoken_feedback().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, SelectToSpeakTopView) {
+  EnableSelectToSpeak(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(select_to_speak_top_view());
+  EXPECT_TRUE(IsChecked(select_to_speak_top_view()));
+
+  ClickView(select_to_speak_top_view());
+  EXPECT_FALSE(IsChecked(select_to_speak_top_view()));
+  EXPECT_FALSE(controller()->select_to_speak().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, DictationTopView) {
+  EnableDictation(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(dictation_top_view());
+  EXPECT_TRUE(IsChecked(dictation_top_view()));
+
+  ClickView(dictation_top_view());
+  EXPECT_FALSE(IsChecked(dictation_top_view()));
+  EXPECT_FALSE(controller()->dictation().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, HighContrastTopView) {
+  EnableHighContrast(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(high_contrast_top_view());
+  EXPECT_TRUE(IsChecked(high_contrast_top_view()));
+
+  ClickView(high_contrast_top_view());
+  EXPECT_FALSE(IsChecked(high_contrast_top_view()));
+  EXPECT_FALSE(controller()->high_contrast().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, ScreenMagnifierTopView) {
+  Shell::Get()->accessibility_delegate()->SetMagnifierEnabled(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(screen_magnifier_top_view());
+  EXPECT_TRUE(IsChecked(screen_magnifier_top_view()));
+
+  ClickView(screen_magnifier_top_view());
+  // The test accessibility delegate doesn't notify observers of changes, so do
+  // it manually.
+  controller()->NotifyAccessibilityStatusChanged();
+
+  EXPECT_FALSE(IsChecked(screen_magnifier_top_view()));
+  EXPECT_FALSE(Shell::Get()->accessibility_delegate()->IsMagnifierEnabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, DockedMagnifierTopView) {
+  SetDockedMagnifierEnabled(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(docked_magnifier_top_view());
+  EXPECT_TRUE(IsChecked(docked_magnifier_top_view()));
+
+  ClickView(docked_magnifier_top_view());
+  EXPECT_FALSE(IsChecked(docked_magnifier_top_view()));
+  EXPECT_FALSE(Shell::Get()->docked_magnifier_controller()->GetEnabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, LargeCursorTopView) {
+  EnableLargeCursor(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(large_cursor_top_view());
+  EXPECT_TRUE(IsChecked(large_cursor_top_view()));
+
+  ClickView(large_cursor_top_view());
+  EXPECT_FALSE(IsChecked(large_cursor_top_view()));
+  EXPECT_FALSE(controller()->large_cursor().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, LiveCaptionTopView) {
+  EnableLiveCaption(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(live_caption_top_view());
+  EXPECT_TRUE(IsChecked(live_caption_top_view()));
+
+  ClickView(live_caption_top_view());
+  EXPECT_FALSE(IsChecked(live_caption_top_view()));
+  EXPECT_FALSE(controller()->live_caption().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, AutoClickTopView) {
+  EnableAutoclick(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(autoclick_top_view());
+  EXPECT_TRUE(IsChecked(autoclick_top_view()));
+
+  ClickView(autoclick_top_view());
+  EXPECT_FALSE(IsChecked(autoclick_top_view()));
+  EXPECT_FALSE(controller()->autoclick().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, VirtualKeyboardTopView) {
+  EnableVirtualKeyboard(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(virtual_keyboard_top_view());
+  EXPECT_TRUE(IsChecked(virtual_keyboard_top_view()));
+
+  ClickView(virtual_keyboard_top_view());
+  EXPECT_FALSE(IsChecked(virtual_keyboard_top_view()));
+  EXPECT_FALSE(controller()->virtual_keyboard().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, MonoAudioTopView) {
+  EnableMonoAudio(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(mono_audio_top_view());
+  EXPECT_TRUE(IsChecked(mono_audio_top_view()));
+
+  ClickView(mono_audio_top_view());
+  EXPECT_FALSE(IsChecked(mono_audio_top_view()));
+  EXPECT_FALSE(controller()->mono_audio().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, CaretHighlightTopView) {
+  SetCaretHighlightEnabled(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(caret_highlight_top_view());
+  EXPECT_TRUE(IsChecked(caret_highlight_top_view()));
+
+  ClickView(caret_highlight_top_view());
+  EXPECT_FALSE(IsChecked(caret_highlight_top_view()));
+  EXPECT_FALSE(controller()->caret_highlight().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, HighlightMouseCursorTopView) {
+  SetCursorHighlightEnabled(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(highlight_mouse_cursor_top_view());
+  EXPECT_TRUE(IsChecked(highlight_mouse_cursor_top_view()));
+
+  ClickView(highlight_mouse_cursor_top_view());
+  EXPECT_FALSE(IsChecked(highlight_mouse_cursor_top_view()));
+  EXPECT_FALSE(controller()->cursor_highlight().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, HighlightKeyboardFocusTopView) {
+  SetFocusHighlightEnabled(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(highlight_keyboard_focus_top_view());
+  EXPECT_TRUE(IsChecked(highlight_keyboard_focus_top_view()));
+
+  ClickView(highlight_keyboard_focus_top_view());
+  EXPECT_FALSE(IsChecked(highlight_keyboard_focus_top_view()));
+  EXPECT_FALSE(controller()->focus_highlight().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, StickyKeysTopView) {
+  EnableStickyKeys(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(sticky_keys_top_view());
+  EXPECT_TRUE(IsChecked(sticky_keys_top_view()));
+
+  ClickView(sticky_keys_top_view());
+  EXPECT_FALSE(IsChecked(sticky_keys_top_view()));
+  EXPECT_FALSE(controller()->sticky_keys().enabled());
+}
+
+TEST_F(AccessibilityDetailedViewQsRevampTest, SwitchAccessTopView) {
+  // Don't show the confirmation dialog when disabling switch access, so the
+  // feature will be disabled immediately.
+  controller()->DisableSwitchAccessDisableConfirmationDialogTesting();
+
+  EnableSwitchAccess(true);
+  CreateDetailedMenu();
+  ASSERT_TRUE(switch_access_top_view());
+  EXPECT_TRUE(IsChecked(switch_access_top_view()));
+
+  ClickView(switch_access_top_view());
+  EXPECT_FALSE(IsChecked(switch_access_top_view()));
+  EXPECT_FALSE(controller()->switch_access().enabled());
+}
+
 TEST_F(AccessibilityDetailedViewTest, CheckMenuVisibilityOnDetailMenu) {
   // Except help & settings, others should be kept the same
   // in LOGIN | NOT LOGIN | LOCKED. https://crbug.com/632107.
diff --git a/ash/system/privacy/screen_capture_observer.h b/ash/system/privacy/screen_capture_observer.h
index df5ec95..8b17ef23 100644
--- a/ash/system/privacy/screen_capture_observer.h
+++ b/ash/system/privacy/screen_capture_observer.h
@@ -16,10 +16,8 @@
   // Called when screen capture is started.
   // |stop_callback| is a callback to stop the stream.
   // |source_callback| is a callback to change the desktop capture source.
-  // These must be base::RepeatingCallbacks so that they can be passed to all
-  // observers.
   virtual void OnScreenCaptureStart(
-      const base::RepeatingClosure& stop_callback,
+      base::OnceClosure stop_callback,
       const base::RepeatingClosure& source_callback,
       const std::u16string& screen_capture_status) = 0;
 
diff --git a/ash/system/privacy/screen_security_controller.cc b/ash/system/privacy/screen_security_controller.cc
index 4b6ad1f..b438098 100644
--- a/ash/system/privacy/screen_security_controller.cc
+++ b/ash/system/privacy/screen_security_controller.cc
@@ -143,10 +143,10 @@
 }
 
 void ScreenSecurityController::OnScreenCaptureStart(
-    const base::RepeatingClosure& stop_callback,
+    base::OnceClosure stop_callback,
     const base::RepeatingClosure& source_callback,
     const std::u16string& screen_capture_status) {
-  capture_stop_callbacks_.push_back(stop_callback);
+  capture_stop_callbacks_.emplace_back(std::move(stop_callback));
   change_source_callback_ = source_callback;
 
   // We do not want to show the screen capture notification and the chromecast
@@ -167,7 +167,7 @@
 }
 
 void ScreenSecurityController::OnScreenShareStart(
-    const base::RepeatingClosure& stop_callback,
+    base::OnceClosure stop_callback,
     const std::u16string& helper_name) {
   share_stop_callbacks_.emplace_back(std::move(stop_callback));
 
diff --git a/ash/system/privacy/screen_security_controller.h b/ash/system/privacy/screen_security_controller.h
index 8830395..869e3bb 100644
--- a/ash/system/privacy/screen_security_controller.h
+++ b/ash/system/privacy/screen_security_controller.h
@@ -44,13 +44,13 @@
 
   // ScreenCaptureObserver:
   void OnScreenCaptureStart(
-      const base::RepeatingClosure& stop_callback,
+      base::OnceClosure stop_callback,
       const base::RepeatingClosure& source_callback,
       const std::u16string& screen_capture_status) override;
   void OnScreenCaptureStop() override;
 
   // ScreenShareObserver:
-  void OnScreenShareStart(const base::RepeatingClosure& stop_callback,
+  void OnScreenShareStart(base::OnceClosure stop_callback,
                           const std::u16string& helper_name) override;
   void OnScreenShareStop() override;
 
diff --git a/ash/system/privacy/screen_share_observer.h b/ash/system/privacy/screen_share_observer.h
index 91f92d9..07f29f7c 100644
--- a/ash/system/privacy/screen_share_observer.h
+++ b/ash/system/privacy/screen_share_observer.h
@@ -14,16 +14,14 @@
 class ScreenShareObserver {
  public:
   // Called when screen share is started.
-  // |stop_callback| must be a base::RepeatingCallback so that it can be passed
-  // to all observers.
-  virtual void OnScreenShareStart(const base::RepeatingClosure& stop_callback,
+  virtual void OnScreenShareStart(base::OnceClosure stop_callback,
                                   const std::u16string& helper_name) = 0;
 
   // Called when screen share is stopped.
   virtual void OnScreenShareStop() = 0;
 
  protected:
-  virtual ~ScreenShareObserver() {}
+  virtual ~ScreenShareObserver() = default;
 };
 
 }  // namespace ash
diff --git a/ash/system/privacy/screen_switch_check_controller.cc b/ash/system/privacy/screen_switch_check_controller.cc
index dee109a..823d9ec66 100644
--- a/ash/system/privacy/screen_switch_check_controller.cc
+++ b/ash/system/privacy/screen_switch_check_controller.cc
@@ -22,7 +22,7 @@
 // callback with the result.
 class CancelCastingDialog : public views::DialogDelegateView {
  public:
-  CancelCastingDialog(base::OnceCallback<void(bool)> callback)
+  explicit CancelCastingDialog(base::OnceCallback<void(bool)> callback)
       : callback_(std::move(callback)) {
     AddChildView(new views::MessageBoxView(
         l10n_util::GetStringUTF16(IDS_DESKTOP_CASTING_ACTIVE_MESSAGE)));
@@ -87,7 +87,7 @@
 }
 
 void ScreenSwitchCheckController::OnScreenCaptureStart(
-    const base::RepeatingClosure& stop_callback,
+    base::OnceClosure stop_callback,
     const base::RepeatingClosure& source_callback,
     const std::u16string& screen_capture_status) {
   has_capture_ = true;
@@ -100,7 +100,7 @@
 }
 
 void ScreenSwitchCheckController::OnScreenShareStart(
-    const base::RepeatingClosure& stop_callback,
+    base::OnceClosure stop_callback,
     const std::u16string& helper_name) {
   has_share_ = true;
 }
diff --git a/ash/system/privacy/screen_switch_check_controller.h b/ash/system/privacy/screen_switch_check_controller.h
index f8a22d10..e203b97 100644
--- a/ash/system/privacy/screen_switch_check_controller.h
+++ b/ash/system/privacy/screen_switch_check_controller.h
@@ -31,13 +31,13 @@
  private:
   // ScreenCaptureObserver:
   void OnScreenCaptureStart(
-      const base::RepeatingClosure& stop_callback,
+      base::OnceClosure stop_callback,
       const base::RepeatingClosure& source_callback,
       const std::u16string& screen_capture_status) override;
   void OnScreenCaptureStop() override;
 
   // ScreenShareObserver:
-  void OnScreenShareStart(const base::RepeatingClosure& stop_callback,
+  void OnScreenShareStart(base::OnceClosure stop_callback,
                           const std::u16string& helper_name) override;
   void OnScreenShareStop() override;
 
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index 06bc37d2..776c692 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -19,6 +19,7 @@
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/tray/tri_view.h"
 #include "base/bind.h"
+#include "base/check.h"
 #include "base/containers/adapters.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -369,6 +370,7 @@
 TrayDetailedView::~TrayDetailedView() = default;
 
 void TrayDetailedView::OnViewClicked(views::View* sender) {
+  DCHECK(sender);
   HandleViewClicked(sender);
 }
 
diff --git a/ash/system/tray/view_click_listener.h b/ash/system/tray/view_click_listener.h
index e3eb11d..505ea4a 100644
--- a/ash/system/tray/view_click_listener.h
+++ b/ash/system/tray/view_click_listener.h
@@ -15,6 +15,7 @@
 
 class ASH_EXPORT ViewClickListener {
  public:
+  // Called when `sender` is clicked. `sender` is non-null.
   virtual void OnViewClicked(views::View* sender) = 0;
 
  protected:
diff --git a/ash/webui/personalization_app/resources/js/ambient/album_list_element.html b/ash/webui/personalization_app/resources/js/ambient/album_list_element.html
index d515608..ba83491 100644
--- a/ash/webui/personalization_app/resources/js/ambient/album_list_element.html
+++ b/ash/webui/personalization_app/resources/js/ambient/album_list_element.html
@@ -12,13 +12,12 @@
 <iron-list aria-setsize$="[[albums.length]]" as="album" grid id="grid"
     items="[[albums]]" role="listbox">
   <template>
-    <wallpaper-grid-item aria-label$="[[album.title]]"
+    <wallpaper-grid-item aria-label$="[[album.title]]" 
         aria-posinset$="[[getAriaIndex_(index)]]"
         class$="[[getAlbumItemClass_(album, albums)]]"
         index="[[index]]"
         is-google-photos="[[isGooglePhotos_(topicSource)]]"
-        on-click="onAlbumSelected_"
-        on-keypress="onAlbumSelected_"
+        on-wallpaper-grid-item-selected="onAlbumSelected_"
         primary-text="[[album.title]]"
         role="option"
         secondary-text="[[getSecondaryText_(album, topicSource)]]"
diff --git a/ash/webui/personalization_app/resources/js/ambient/album_list_element.ts b/ash/webui/personalization_app/resources/js/ambient/album_list_element.ts
index c1e88716..da43159 100644
--- a/ash/webui/personalization_app/resources/js/ambient/album_list_element.ts
+++ b/ash/webui/personalization_app/resources/js/ambient/album_list_element.ts
@@ -14,7 +14,7 @@
 
 import {AmbientModeAlbum, TopicSource} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
-import {getCountText, isSelectionEvent} from '../utils.js';
+import {getCountText} from '../utils.js';
 
 import {getTemplate} from './album_list_element.html.js';
 import {isRecentHighlightsAlbum} from './utils.js';
@@ -61,12 +61,10 @@
 
   /** Invoked on selection of an album. */
   private onAlbumSelected_(e: Event&{model: {album: AmbientModeAlbum}}) {
-    if (isSelectionEvent(e)) {
-      e.model.album.checked = !e.model.album.checked;
-      this.dispatchEvent(new CustomEvent(
-          'album_selected_changed',
-          {bubbles: true, composed: true, detail: {album: e.model.album}}));
-    }
+    e.model.album.checked = !e.model.album.checked;
+    this.dispatchEvent(new CustomEvent(
+        'album_selected_changed',
+        {bubbles: true, composed: true, detail: {album: e.model.album}}));
   }
 
   private isAlbumSelected_(
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.html b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.html
index c0a883660..2adb478d 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.html
@@ -23,14 +23,13 @@
           aria-label$="[[getAlbumAriaLabel_(album)]]"
           aria-posinset$="[[getAlbumAriaIndex_(index)]]"
           class="album"
-          src="[[album.preview]]"
           index="[[index]]"
           is-google-photos
-          on-click="onAlbumSelected_"
-          on-keypress="onAlbumSelected_"
+          on-wallpaper-grid-item-selected="onAlbumSelected_"
           primary-text="[[album.title]]"
           role="listitem"
           secondary-text="[[getSecondaryText_(album)]]"
+          src="[[album.preview]]"
           tabindex$="[[tabIndex]]">
       </wallpaper-grid-item>
     </template>
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.ts
index 6c55f95..48fe6c0 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_albums_element.ts
@@ -21,7 +21,7 @@
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {PersonalizationStateError} from '../personalization_state.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
-import {getCountText, isSelectionEvent} from '../utils.js';
+import {getCountText} from '../utils.js';
 
 import {getTemplate} from './google_photos_albums_element.html.js';
 import {getLoadingPlaceholders} from './utils.js';
@@ -126,7 +126,7 @@
   /** Invoked on selection of an album. */
   private onAlbumSelected_(e: Event&{model: {album: GooglePhotosAlbum}}) {
     assert(e.model.album);
-    if (!this.isAlbumPlaceholder_(e.model.album) && isSelectionEvent(e)) {
+    if (!this.isAlbumPlaceholder_(e.model.album)) {
       PersonalizationRouter.instance().selectGooglePhotosAlbum(e.model.album);
     }
   }
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.html b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.html
index 5d17fc9..e7ede95 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.html
@@ -16,17 +16,16 @@
       role="listbox">
     <template>
       <wallpaper-grid-item
-          aria-disabled$="[[getPhotoAriaDisabled_(photo)]]"
           aria-label$="[[getPhotoAriaLabel_(photo)]]"
           aria-posinset$="[[getPhotoAriaIndex_(index)]]"
           class="photo"
-          src="[[photo.url]]"
+          disabled="[[isPhotoPlaceholder_(photo)]]"
           index="[[index]]"
           is-google-photos
-          on-click="onPhotoSelected_"
-          on-keypress="onPhotoSelected_"
+          on-wallpaper-grid-item-selected="onPhotoSelected_"
           role="option"
           selected="[[isPhotoSelected_(photo, currentSelected_, pendingSelected_)]]"
+          src="[[photo.url]]"
           tabindex$="[[tabIndex]]">
       </wallpaper-grid-item>
     </template>
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.ts
index f49785d..12aadc9 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_by_album_id_element.ts
@@ -21,13 +21,13 @@
 import {CurrentWallpaper, GooglePhotosAlbum, GooglePhotosPhoto, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {PersonalizationStateError} from '../personalization_state.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
-import {isSelectionEvent} from '../utils.js';
 
 import {DisplayableImage} from './constants.js';
 import {recordWallpaperGooglePhotosSourceUMA, WallpaperGooglePhotosSource} from './google_photos_metrics_logger.js';
 import {getTemplate} from './google_photos_photos_by_album_id_element.html.js';
 import {getLoadingPlaceholders, isGooglePhotosPhoto, isImageAMatchForKey, isImageEqualToSelected} from './utils.js';
 import {fetchGooglePhotosAlbum, selectWallpaper} from './wallpaper_controller.js';
+import {WallpaperGridItemSelectedEvent} from './wallpaper_grid_item_element.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
 const ERROR_ID = 'GooglePhotosByAlbumId';
@@ -278,23 +278,16 @@
     }
   }
 
-  /** Invoked on selection of a photo. */
-  private onPhotoSelected_(e: Event&{model: {photo: GooglePhotosPhoto}}) {
-    assert(e.model.photo);
-    if (!this.isPhotoPlaceholder_(e.model.photo) && isSelectionEvent(e)) {
+  /** Invoked on selection of a photo. `e.model.photo` is added by iron-list. */
+  private onPhotoSelected_(e: WallpaperGridItemSelectedEvent&
+                           {model: {photo: GooglePhotosPhoto}}) {
+    assert(e.model.photo, 'google photos album photo selected event has photo');
+    if (!this.isPhotoPlaceholder_(e.model.photo)) {
       selectWallpaper(e.model.photo, this.wallpaperProvider_, this.getStore());
       recordWallpaperGooglePhotosSourceUMA(WallpaperGooglePhotosSource.ALBUMS);
     }
   }
 
-  /**
-   * Returns 'true' or 'false' depending on whether the specified |photo| is
-   * a placeholder.
-   */
-  private getPhotoAriaDisabled_(photo: GooglePhotosPhoto|null): string {
-    return this.isPhotoPlaceholder_(photo).toString();
-  }
-
   /** Returns the aria label for the specified |photo|. */
   private getPhotoAriaLabel_(photo: GooglePhotosPhoto|null): string|undefined {
     if (photo) {
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.html b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.html
index 92799424..0439cfe 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.html
@@ -69,19 +69,18 @@
         <div class="photos">
           <template is="dom-repeat" items="[[row]]" as="photo">
             <wallpaper-grid-item
-                aria-disabled$="[[getPhotoAriaDisabled_(photo)]]"
                 aria-label$="[[getPhotoAriaLabel_(photo)]]"
                 aria-posinset$="[[getPhotoAriaIndex_(photo.index)]]"
                 class="photo"
                 colindex$="[[index]]"
-                src="[[photo.url]]"
+                disabled="[[isPhotoPlaceholder_(photo)]]"
                 index="[[photo.index]]"
                 is-google-photos
-                on-click="onPhotoSelected_"
-                on-keypress="onPhotoSelected_"
+                on-wallpaper-grid-item-selected="onPhotoSelected_"
                 photoindex$="[[photo.index]]"
                 role="option"
                 selected="[[isPhotoSelected_(photo, currentSelected_, pendingSelected_)]]"
+                src="[[photo.url]]"
                 tabindex="-1">
             </wallpaper-grid-item>
           </template>
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.ts
index 12d2d96..4793b7b 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/google_photos_photos_element.ts
@@ -20,13 +20,14 @@
 import {CurrentWallpaper, GooglePhotosPhoto, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {PersonalizationStateError} from '../personalization_state.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
-import {getNumberOfGridItemsPerRow, isNonEmptyArray, isSelectionEvent} from '../utils.js';
+import {getNumberOfGridItemsPerRow, isNonEmptyArray} from '../utils.js';
 
 import {DisplayableImage} from './constants.js';
 import {recordWallpaperGooglePhotosSourceUMA, WallpaperGooglePhotosSource} from './google_photos_metrics_logger.js';
 import {getTemplate} from './google_photos_photos_element.html.js';
 import {getLoadingPlaceholders, isGooglePhotosPhoto, isImageAMatchForKey, isImageEqualToSelected} from './utils.js';
 import {fetchGooglePhotosPhotos, selectWallpaper} from './wallpaper_controller.js';
+import {WallpaperGridItemSelectedEvent} from './wallpaper_grid_item_element.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
 const ERROR_ID = 'GooglePhotosPhotos';
@@ -369,10 +370,11 @@
     }
   }
 
-  /** Invoked on selection of a photo. */
-  private onPhotoSelected_(e: Event&{model: {photo: GooglePhotosPhoto}}) {
-    assert(e.model.photo);
-    if (!this.isPhotoPlaceholder_(e.model.photo) && isSelectionEvent(e)) {
+  /** Invoked on selection of a photo. `e.model.photo` is added by iron-list. */
+  private onPhotoSelected_(e: WallpaperGridItemSelectedEvent&
+                           {model: {photo: GooglePhotosPhoto}}) {
+    assert(e.model.photo, 'google photos photos selected event has photo');
+    if (!this.isPhotoPlaceholder_(e.model.photo)) {
       selectWallpaper(e.model.photo, this.wallpaperProvider_, this.getStore());
       recordWallpaperGooglePhotosSourceUMA(WallpaperGooglePhotosSource.PHOTOS);
     }
@@ -527,14 +529,6 @@
     return getPlaceholders().length * getNumberOfGridItemsPerRow();
   }
 
-  /**
-   * Returns 'true' or 'false' depending on whether the specified |photo| is
-   * a placeholder.
-   */
-  private getPhotoAriaDisabled_(photo: GooglePhotosPhoto|null): string {
-    return this.isPhotoPlaceholder_(photo).toString();
-  }
-
   /** Returns the aria label for the specified |photo|. */
   private getPhotoAriaLabel_(photo: GooglePhotosPhoto|null): string|undefined {
     if (photo) {
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.html b/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.html
index 7ea39e2..f00560e 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.html
@@ -16,13 +16,12 @@
       aria-setsize$="[[imagesToDisplay_.length]]">
     <template>
       <wallpaper-grid-item
-          aria-disabled$="[[isImageLoading_(item, imageDataLoading_)]]"
           aria-label$="[[getAriaLabel_(item, imageDataLoading_)]]"
           aria-posinset$="[[getAriaIndex_(index)]]"
           data-id$="[[getImageDataId_(item)]]"
+          disabled="[[isImageLoading_(item, imageDataLoading_)]]"
           index="[[index]]"
-          on-click="onImageSelected_"
-          on-keypress="onImageSelected_"
+          on-wallpaper-grid-item-selected="onImageSelected_"
           role="option"
           selected="[[isImageSelected_(item, currentSelected_, pendingSelected_)]]"
           src="[[getImageData_(item, imageData_, imageDataLoading_)]]"
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.ts
index d68cb1e..5a344212 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/local_images_element.ts
@@ -15,19 +15,20 @@
 import '../../common/icons.html.js';
 import '../../css/common.css.js';
 
-import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
+import {assert} from 'chrome://resources/js/assert.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {afterNextRender} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CurrentWallpaper, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
-import {isImageDataUrl, isSelectionEvent} from '../utils.js';
+import {isImageDataUrl} from '../utils.js';
 
 import {DefaultImageSymbol, DisplayableImage, kDefaultImageSymbol} from './constants.js';
 import {getTemplate} from './local_images_element.html.js';
 import {getPathOrSymbol, isDefaultImage, isFilePath} from './utils.js';
 import {fetchLocalData, getDefaultImageThumbnail, selectWallpaper} from './wallpaper_controller.js';
+import {WallpaperGridItemSelectedEvent} from './wallpaper_grid_item_element.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
 
@@ -231,27 +232,13 @@
     return isFilePath(image) ? image.path : image.toString();
   }
 
-  private onImageSelected_(event: Event) {
-    if (!isSelectionEvent(event)) {
-      return;
-    }
-    const dataId = (event.currentTarget as HTMLElement).dataset['id'];
+  private onImageSelected_(event: WallpaperGridItemSelectedEvent&
+                           {model: {item: FilePath | DefaultImageSymbol}}) {
     assert(
-        typeof dataId === 'string' && dataId.length > 0,
-        'image data id is required');
-    const image = this.images_!.find(image => {
-      if (isFilePath(image)) {
-        return dataId === image.path;
-      }
-      assert(
-          image === kDefaultImageSymbol, 'only one symbol should be present');
-      return dataId === image.toString();
-    });
-    if (!image) {
-      assertNotReached('Image with that path not found');
-      return;
-    }
-    selectWallpaper(image, this.wallpaperProvider_, this.getStore());
+        event.model.item === kDefaultImageSymbol ||
+            isFilePath(event.model.item),
+        'local image is a file path or default image');
+    selectWallpaper(event.model.item, this.wallpaperProvider_, this.getStore());
   }
 
   private getAriaIndex_(i: number): number {
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.html b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.html
index 98b5ae4..6aaf0448 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.html
@@ -50,6 +50,10 @@
     width: 100%;
   }
 
+  :host([aria-disabled='true']) .item {
+    cursor: default;
+  }
+
   :host(:focus-visible) .item {
     outline: 2px solid var(--cros-focus-ring-color);
   }
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.ts
index 7bf6433c..34ae942 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_grid_item_element.ts
@@ -13,6 +13,8 @@
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {isSelectionEvent} from '../utils.js';
+
 import {getLoadingPlaceholderAnimationDelay} from './utils.js';
 import {getTemplate} from './wallpaper_grid_item_element.html.js';
 
@@ -36,6 +38,27 @@
        !imageStatus.includes(ImageStatus.ERROR));
 }
 
+const wallpaperGridItemSelectedEventName = 'wallpaper-grid-item-selected';
+
+export class WallpaperGridItemSelectedEvent extends CustomEvent<null> {
+  constructor() {
+    super(
+        wallpaperGridItemSelectedEventName,
+        {
+          bubbles: true,
+          composed: true,
+          detail: null,
+        },
+    );
+  }
+}
+
+declare global {
+  interface HTMLElementEventMap {
+    [wallpaperGridItemSelectedEventName]: WallpaperGridItemSelectedEvent;
+  }
+}
+
 export class WallpaperGridItem extends PolymerElement {
   static get is(): 'wallpaper-grid-item' {
     return 'wallpaper-grid-item';
@@ -66,6 +89,12 @@
         observer: 'onSelectedChanged_',
       },
 
+      disabled: {
+        type: Boolean,
+        value: false,
+        observer: 'onDisabledChanged_',
+      },
+
       imageStatus_: {
         type: Array,
         value() {
@@ -105,9 +134,36 @@
    */
   selected: boolean|undefined;
 
+  /**
+   * Whether the grid item is currently disabled. Automatically sets the
+   * aria-disabled property.
+   * @default false
+   */
+  disabled: boolean;
+
   // Track if images are loaded, failed, or ready to display.
   private imageStatus_: ImageStatus[];
 
+  override ready() {
+    super.ready();
+    this.addEventListener('click', this.onUserSelection_);
+    this.addEventListener('keydown', this.onUserSelection_);
+  }
+
+  private onUserSelection_(event: MouseEvent|KeyboardEvent) {
+    // Ignore extraneous events and let them continue.
+    // Also ignore click and keydown events if this grid item is disabled.
+    // These events will continue to propagate up in case someone else is
+    // interested that this item was interacted with.
+    if (!isSelectionEvent(event) || this.disabled) {
+      return;
+    }
+
+    event.preventDefault();
+    event.stopPropagation();
+    this.dispatchEvent(new WallpaperGridItemSelectedEvent());
+  }
+
   // Invoked on changes to |imageSrc|.
   private onImageSrcChanged_(
       src: Url|Url[]|undefined, old: Url|Url[]|undefined) {
@@ -132,6 +188,10 @@
     }
   }
 
+  private onDisabledChanged_(disabled: boolean) {
+    this.setAttribute('aria-disabled', disabled.toString());
+  }
+
   private onImageStatusChanged_(imageStatus: ImageStatus[]) {
     if (shouldShowPlaceholder(imageStatus)) {
       this.setAttribute('placeholder', '');
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html
index 806e31d8..be88a87 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html
@@ -9,14 +9,11 @@
         aria-setsize$="[[tiles_.length]]">
       <template>
         <wallpaper-grid-item
-            aria-disabled$="[[isLoadingTile_(item)]]"
             aria-label$="[[getAriaLabel_(item)]]"
             aria-posinset$="[[getAriaIndex_(index)]]"
-            data-asset-id$="[[item.assetId]]"
-            data-unit-id$="[[item.unitId]]"
+            disabled="[[isLoadingTile_(item)]]"
             index="[[index]]"
-            on-click="onImageSelected_"
-            on-keypress="onImageSelected_"
+            on-wallpaper-grid-item-selected="onImageSelected_"
             role="option"
             selected="[[isTileSelected_(item, selectedAssetId_, pendingSelectedAssetId_)]]"
             src="[[item.preview]]"
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts
index 78145dd0..f895051 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts
@@ -18,11 +18,12 @@
 import {CurrentWallpaper, OnlineImageType, WallpaperCollection, WallpaperImage, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
-import {isNonEmptyArray, isSelectionEvent} from '../utils.js';
+import {isNonEmptyArray} from '../utils.js';
 
 import {ImageTile} from './constants.js';
 import {getLoadingPlaceholderAnimationDelay, getLoadingPlaceholders, isWallpaperImage} from './utils.js';
 import {selectWallpaper} from './wallpaper_controller.js';
+import {WallpaperGridItemSelectedEvent} from './wallpaper_grid_item_element';
 import {getTemplate} from './wallpaper_images_element.html.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
@@ -307,18 +308,14 @@
     }
   }
 
-  private onImageSelected_(e: Event) {
-    if (!isSelectionEvent(e)) {
-      return;
-    }
-    const imgElement = e.currentTarget as HTMLImageElement;
-    const assetId = imgElement.dataset['assetId'];
-    assert(assetId, 'assetId not found');
+  private onImageSelected_(e: WallpaperGridItemSelectedEvent&
+                           {model: {item: ImageTile}}) {
+    const assetId = e.model.item.assetId;
+    assert(assetId && typeof assetId === 'bigint', 'assetId not found');
     const images = this.images_[this.collectionId]!;
     assert(isNonEmptyArray(images));
-    const selectedImage =
-        images.find(choice => choice.assetId.toString() === assetId);
-    assert(selectedImage);
+    const selectedImage = images.find(choice => choice.assetId === assetId);
+    assert(selectedImage, 'could not find selected image');
     selectWallpaper(selectedImage, getWallpaperProvider(), this.getStore());
   }
 
diff --git a/ash/webui/shortcut_customization_ui/backend/BUILD.gn b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
index 1c4219e..64d373d 100644
--- a/ash/webui/shortcut_customization_ui/backend/BUILD.gn
+++ b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
@@ -17,6 +17,7 @@
     "//ash/public/cpp",
     "//ash/public/mojom",
     "//ash/webui/shortcut_customization_ui/mojom",
+    "//ui/events/devices",
   ]
 }
 
@@ -36,5 +37,7 @@
     "//chromeos/ash/components:test_support",
     "//content/test:test_support",
     "//testing/gtest",
+    "//ui/events/devices",
+    "//ui/events/devices:test_support",
   ]
 }
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
index df3cf21..d026709 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
@@ -15,6 +15,8 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/input_device.h"
 
 namespace ash {
 
@@ -40,13 +42,20 @@
 AcceleratorConfigurationProvider::AcceleratorConfigurationProvider()
     : ash_accelerator_configuration_(
           Shell::Get()->ash_accelerator_configuration()) {
+  // Observe connected keyboard events.
+  ui::DeviceDataManager::GetInstance()->AddObserver(this);
+
   ash_accelerator_configuration_->AddAcceleratorsUpdatedCallback(
       base::BindRepeating(
           &AcceleratorConfigurationProvider::OnAcceleratorsUpdated,
           weak_ptr_factory_.GetWeakPtr()));
+
+  UpdateKeyboards();
 }
 
-AcceleratorConfigurationProvider::~AcceleratorConfigurationProvider() = default;
+AcceleratorConfigurationProvider::~AcceleratorConfigurationProvider() {
+  ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
+}
 
 void AcceleratorConfigurationProvider::IsMutable(
     ash::mojom::AcceleratorSource source,
@@ -85,6 +94,13 @@
   std::move(callback).Run(std::move(accelerator_config));
 }
 
+void AcceleratorConfigurationProvider::OnInputDeviceConfigurationChanged(
+    uint8_t input_device_types) {
+  if (input_device_types & (ui::InputDeviceEventObserver::kKeyboard)) {
+    UpdateKeyboards();
+  }
+}
+
 void AcceleratorConfigurationProvider::BindInterface(
     mojo::PendingReceiver<
         shortcut_customization::mojom::AcceleratorConfigurationProvider>
@@ -102,6 +118,14 @@
   return mojom::AcceleratorType::kDefault;
 }
 
+void AcceleratorConfigurationProvider::UpdateKeyboards() {
+  ui::DeviceDataManager* device_data_manager =
+      ui::DeviceDataManager::GetInstance();
+  DCHECK(device_data_manager);
+
+  connected_keyboards_ = device_data_manager->GetKeyboardDevices();
+}
+
 void AcceleratorConfigurationProvider::OnAcceleratorsUpdated(
     mojom::AcceleratorSource source,
     const ActionIdToAcceleratorsMap& mapping) {
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
index 4333487f..47220e17e 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
@@ -12,12 +12,15 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/input_device_event_observer.h"
 
 namespace ash {
 namespace shortcut_ui {
 
 class AcceleratorConfigurationProvider
-    : shortcut_customization::mojom::AcceleratorConfigurationProvider {
+    : public shortcut_customization::mojom::AcceleratorConfigurationProvider,
+      public ui::InputDeviceEventObserver {
  public:
   using AcceleratorConfigurationMap =
       base::flat_map<mojom::AcceleratorSource,
@@ -40,6 +43,9 @@
                  IsMutableCallback callback) override;
   void GetAccelerators(GetAcceleratorsCallback callback) override;
 
+  // ui::InputDeviceEventObserver:
+  void OnInputDeviceConfigurationChanged(uint8_t input_device_types) override;
+
   void BindInterface(
       mojo::PendingReceiver<
           shortcut_customization::mojom::AcceleratorConfigurationProvider>
@@ -53,11 +59,16 @@
 
   mojom::AcceleratorType GetAcceleratorType(ui::Accelerator accelerator);
 
+  void UpdateKeyboards();
+
   std::vector<AcceleratorInfo> accelerator_infos_;
 
   std::map<AcceleratorActionId, std::vector<AcceleratorInfo>>
       id_to_accelerator_info_;
 
+  // Stores all connected keyboards.
+  std::vector<ui::InputDevice> connected_keyboards_;
+
   mojo::Receiver<
       shortcut_customization::mojom::AcceleratorConfigurationProvider>
       receiver_{this};
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
index 4ae7783ab..681f2bb 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -23,6 +23,8 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ui/events/devices/input_device.h"
 
 namespace ash {
 
@@ -50,6 +52,13 @@
          key_display_equals && has_key_event_equals;
 }
 
+void CompareInputDevices(const ui::InputDevice& expected,
+                         const ui::InputDevice& actual) {
+  EXPECT_EQ(expected.type, actual.type);
+  EXPECT_EQ(expected.id, actual.id);
+  EXPECT_EQ(expected.name, actual.name);
+}
+
 void ExpectAllAcceleratorsEqual(
     const base::span<const ash::AcceleratorData>& expected,
     const std::vector<ash::AcceleratorInfo>& actual) {
@@ -128,6 +137,10 @@
                                               expected));
   }
 
+  const std::vector<ui::InputDevice>& GetConnectedKeyboards() {
+    return provider_->connected_keyboards_;
+  }
+
   std::unique_ptr<AcceleratorConfigurationProvider> provider_;
 };
 
@@ -208,6 +221,24 @@
   base::RunLoop().RunUntilIdle();
 }
 
+// TODO(jimmyxgong): Update test to check accelerators sent via Mojo.
+TEST_F(AcceleratorConfigurationProviderTest, ConnectedKeyboardsUpdated) {
+  const std::vector<ui::InputDevice>& devices = GetConnectedKeyboards();
+  EXPECT_TRUE(devices.empty());
+
+  ui::InputDevice expected_test_keyboard(
+      1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard");
+
+  std::vector<ui::InputDevice> keyboard_devices;
+  keyboard_devices.push_back(expected_test_keyboard);
+
+  ui::DeviceDataManagerTestApi().SetKeyboardDevices(keyboard_devices);
+
+  const std::vector<ui::InputDevice>& actual_devices = GetConnectedKeyboards();
+  EXPECT_EQ(1u, actual_devices.size());
+  CompareInputDevices(expected_test_keyboard, actual_devices[0]);
+}
+
 }  // namespace shortcut_ui
 
 }  // namespace ash
diff --git a/ash/wm/desks/templates/saved_desk_library_view.cc b/ash/wm/desks/templates/saved_desk_library_view.cc
index 3a03876..619fdca 100644
--- a/ash/wm/desks/templates/saved_desk_library_view.cc
+++ b/ash/wm/desks/templates/saved_desk_library_view.cc
@@ -23,7 +23,6 @@
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_grid_event_handler.h"
 #include "base/functional/callback_helpers.h"
-#include "base/notreached.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -105,8 +104,8 @@
       case DeskTemplateType::kSaveAndRecall:
         grouped.save_and_recall.push_back(saved_desk);
         break;
+      // Do nothing in the case of an unknown template.
       case DeskTemplateType::kUnknown:
-        NOTREACHED();
         break;
     }
   }
@@ -522,17 +521,14 @@
 
 absl::optional<gfx::Rect> SavedDeskLibraryView::GetDeskPreviewBoundsForLaunch(
     const DeskMiniView* mini_view) {
-  gfx::Transform transform = mini_view->layer()->transform();
-  gfx::Transform inversed;
-  if (!transform.GetInverse(&inversed))
-    return absl::nullopt;
-
   gfx::Rect desk_preview_bounds =
       mini_view->desk_preview()->GetBoundsInScreen();
-  gfx::Point desk_preview_origin =
-      inversed.MapPoint(desk_preview_bounds.origin());
-
-  return gfx::Rect(desk_preview_origin, desk_preview_bounds.size());
+  if (absl::optional<gfx::Point> desk_preview_origin =
+          mini_view->layer()->transform().InverseMapPoint(
+              desk_preview_bounds.origin())) {
+    return gfx::Rect(*desk_preview_origin, desk_preview_bounds.size());
+  }
+  return absl::nullopt;
 }
 
 void SavedDeskLibraryView::AddedToWidget() {
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc
index 883a9ba..63cb172 100644
--- a/ash/wm/desks/templates/saved_desk_unittest.cc
+++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -4513,4 +4513,34 @@
   EXPECT_EQ(2u, GetItemViewsFromDeskLibrary(overview_grid2).size());
 }
 
+// This tests that unknown desk types added into the desk model do not affect
+// the UI.  This is done by adding a template in the normal way and then
+// injecting an unknown type template into the storage model.  The result should
+// be that the saved desk library only shows the normal template.
+TEST_F(SavedDeskTest, UiIgnoresUnknownDeskTypes) {
+  // Add a window.
+  auto test_window = CreateAppWindow();
+
+  // Enter overview.
+  ToggleOverview();
+  ASSERT_TRUE(GetOverviewSession());
+
+  // Save a normal template here.
+  aura::Window* root = Shell::GetPrimaryRootWindow();
+  SavedDeskSaveDeskButton* save_template_button =
+      GetSaveDeskAsTemplateButtonForRoot(root);
+  ASSERT_TRUE(save_template_button);
+  ClickOnView(save_template_button);
+  WaitForDesksTemplatesUI();
+  WaitForLibraryUI();
+
+  // Add unknown type into model.
+  AddEntry(base::GUID::GenerateRandomV4(), "Unknown desk type name\n",
+           base::Time::Now(), DeskTemplateType::kUnknown);
+
+  // Ensure only the normal template appears in the overview grid.
+  OverviewGrid* overview_grid = GetOverviewGridList().front().get();
+  EXPECT_EQ(1u, GetItemViewsFromDeskLibrary(overview_grid).size());
+}
+
 }  // namespace ash
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc
index d3716e1..797e8b2e 100644
--- a/ash/wm/float/float_controller_unittest.cc
+++ b/ash/wm/float/float_controller_unittest.cc
@@ -9,7 +9,6 @@
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
-#include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/test/shell_test_api.h"
@@ -39,6 +38,7 @@
 #include "base/containers/contains.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/ui/base/window_state_type.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "chromeos/ui/wm/constants.h"
 #include "chromeos/ui/wm/features.h"
@@ -60,7 +60,7 @@
 namespace ash {
 
 // Gets the header view for `window` so it can be dragged.
-HeaderView* GetHeaderView(aura::Window* window) {
+chromeos::HeaderView* GetHeaderView(aura::Window* window) {
   // Exiting immersive mode because of float does not seem to trigger a layout
   // like it does in production code. Here we force a layout, otherwise the
   // client view will remain the size of the widget, and dragging it will give
@@ -133,7 +133,7 @@
   std::unique_ptr<aura::Window> window = CreateFloatedWindow();
 
   // Double click on the caption. The window should be maximized now.
-  HeaderView* header_view = GetHeaderView(window.get());
+  chromeos::HeaderView* header_view = GetHeaderView(window.get());
   auto* event_generator = GetEventGenerator();
   event_generator->set_current_screen_location(
       header_view->GetBoundsInScreen().CenterPoint());
@@ -271,7 +271,7 @@
   // does not update the display associated with the cursor, so we have to
   // manually do it here.
   auto* frame = NonClientFrameViewAsh::Get(window.get());
-  HeaderView* header_view = frame->GetHeaderView();
+  chromeos::HeaderView* header_view = frame->GetHeaderView();
   auto* event_generator = GetEventGenerator();
   event_generator->set_current_screen_location(
       header_view->GetBoundsInScreen().CenterPoint());
@@ -908,7 +908,7 @@
   // Start dragging in the center of the header. When moving the touch, the
   // header should move with the touch such that the touch remains in the center
   // of the header.
-  HeaderView* header_view = GetHeaderView(window.get());
+  chromeos::HeaderView* header_view = GetHeaderView(window.get());
   auto* event_generator = GetEventGenerator();
   event_generator->PressTouch(header_view->GetBoundsInScreen().CenterPoint());
 
@@ -929,7 +929,7 @@
   std::unique_ptr<aura::Window> window = CreateFloatedWindow();
 
   // Press the accelerator to maximize before releasing touch.
-  HeaderView* header_view = GetHeaderView(window.get());
+  chromeos::HeaderView* header_view = GetHeaderView(window.get());
   auto* event_generator = GetEventGenerator();
   event_generator->PressTouch(header_view->GetBoundsInScreen().CenterPoint());
   event_generator->MoveTouch(gfx::Point(100, 100));
@@ -994,7 +994,7 @@
 
   // Move the mouse to towards the right edge. Test that on release, it snaps
   // right.
-  HeaderView* header_view = GetHeaderView(window.get());
+  chromeos::HeaderView* header_view = GetHeaderView(window.get());
   auto* event_generator = GetEventGenerator();
   event_generator->set_current_screen_location(
       header_view->GetBoundsInScreen().CenterPoint());
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index 4aab19f..dd62a6c 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 
-#include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_types.h"
@@ -17,6 +16,7 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ui/aura/client/aura_constants.h"
@@ -180,7 +180,7 @@
 
   aura::Window* window() { return widget_->GetNativeWindow(); }
 
-  HeaderView* immersive_delegate() {
+  chromeos::HeaderView* immersive_delegate() {
     return NonClientFrameViewAsh::Get(window())->GetHeaderView();
   }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 2190bec..c9a77607 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -768,7 +768,6 @@
     "task/sequence_manager/task_queue_impl.h",
     "task/sequence_manager/task_queue_selector.cc",
     "task/sequence_manager/task_queue_selector.h",
-    "task/sequence_manager/task_queue_selector_logic.h",
     "task/sequence_manager/task_time_observer.h",
     "task/sequence_manager/tasks.cc",
     "task/sequence_manager/tasks.h",
@@ -1938,6 +1937,7 @@
     # by public //base headers, which requires they be on the include path.
     # TODO(https://crbug.com/841171): Move these back to |deps|.
     public_deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.component.runner",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger",
diff --git a/base/fuchsia/startup_context.cc b/base/fuchsia/startup_context.cc
index 8019b36..e93634c 100644
--- a/base/fuchsia/startup_context.cc
+++ b/base/fuchsia/startup_context.cc
@@ -5,11 +5,13 @@
 #include "base/fuchsia/startup_context.h"
 
 #include <tuple>
+#include <utility>
 
 #include <fuchsia/io/cpp/fidl.h>
 #include <lib/sys/cpp/outgoing_directory.h>
 #include <lib/sys/cpp/service_directory.h>
 
+#include "base/check.h"
 #include "base/check_op.h"
 #include "base/fuchsia/file_utils.h"
 #include "base/fuchsia/fuchsia_logging.h"
@@ -17,6 +19,36 @@
 
 namespace base {
 
+StartupContext::StartupContext(
+    fuchsia::component::runner::ComponentStartInfo start_info) {
+  std::unique_ptr<sys::ServiceDirectory> incoming_services;
+
+  // Component manager generates |flat_namespace|, so things are horribly broken
+  // if |flat_namespace| is malformed.
+  CHECK(start_info.has_ns());
+
+  // Find the /svc directory and wrap it into a sys::ServiceDirectory.
+  auto& namespace_entries = *start_info.mutable_ns();
+  for (auto& entry : namespace_entries) {
+    CHECK(entry.has_path() && entry.has_directory());
+    if (entry.path() == kServiceDirectoryPath) {
+      incoming_services = std::make_unique<sys::ServiceDirectory>(
+          std::move(*entry.mutable_directory()));
+      break;
+    }
+  }
+
+  // If there is no service-directory in the namespace then `incoming_services`
+  // may be null, in which case `svc()` will be null.
+  component_context_ =
+      std::make_unique<sys::ComponentContext>(std::move(incoming_services));
+  if (start_info.has_outgoing_dir()) {
+    outgoing_directory_request_ = std::move(*start_info.mutable_outgoing_dir());
+  }
+}
+
+StartupContext::~StartupContext() = default;
+
 StartupContext::StartupContext(fuchsia::sys::StartupInfo startup_info) {
   std::unique_ptr<sys::ServiceDirectory> incoming_services;
 
@@ -34,51 +66,6 @@
     }
   }
 
-  // TODO(https://crbug.com/933834): Remove these workarounds when we migrate to
-  // the new component manager.
-  if (!incoming_services && startup_info.launch_info.flat_namespace) {
-    LOG(WARNING) << "Falling back to LaunchInfo namespace";
-    for (size_t i = 0;
-         i < startup_info.launch_info.flat_namespace->paths.size(); ++i) {
-      if (startup_info.launch_info.flat_namespace->paths[i] ==
-          kServiceDirectoryPath) {
-        incoming_services = std::make_unique<sys::ServiceDirectory>(
-            std::move(startup_info.launch_info.flat_namespace->directories[i]));
-        break;
-      }
-    }
-  }
-
-  if (!incoming_services && startup_info.launch_info.additional_services) {
-    LOG(WARNING) << "Falling back to additional ServiceList services";
-
-    // Construct a OutgoingDirectory and publish the additional services into
-    // it.
-    additional_services_.Bind(
-        std::move(startup_info.launch_info.additional_services->provider));
-    additional_services_directory_ = std::make_unique<sys::OutgoingDirectory>();
-    for (auto& name : startup_info.launch_info.additional_services->names) {
-      zx_status_t status = additional_services_directory_->AddPublicService(
-          std::make_unique<vfs::Service>([this, name](
-                                             zx::channel channel,
-                                             async_dispatcher_t* dispatcher) {
-            additional_services_->ConnectToService(name, std::move(channel));
-          }),
-          name);
-      ZX_CHECK(status == ZX_OK, status)
-          << "AddPublicService(" << name << ") failed";
-    }
-
-    // Publish those services to the caller as |incoming_services|.
-    fidl::InterfaceHandle<fuchsia::io::Directory> incoming_directory;
-    additional_services_directory_->GetOrCreateDirectory("svc")->Serve(
-        fuchsia::io::OpenFlags::RIGHT_READABLE |
-            fuchsia::io::OpenFlags::RIGHT_WRITABLE,
-        incoming_directory.NewRequest().TakeChannel());
-    incoming_services =
-        std::make_unique<sys::ServiceDirectory>(std::move(incoming_directory));
-  }
-
   if (!incoming_services) {
     LOG(WARNING) << "Component started without a service directory";
 
@@ -96,8 +83,6 @@
       std::move(startup_info.launch_info.directory_request);
 }
 
-StartupContext::~StartupContext() = default;
-
 void StartupContext::ServeOutgoingDirectory() {
   DCHECK(outgoing_directory_request_);
   component_context_->outgoing()->Serve(std::move(outgoing_directory_request_));
diff --git a/base/fuchsia/startup_context.h b/base/fuchsia/startup_context.h
index 263342b1..44c7236 100644
--- a/base/fuchsia/startup_context.h
+++ b/base/fuchsia/startup_context.h
@@ -5,15 +5,17 @@
 #ifndef BASE_FUCHSIA_STARTUP_CONTEXT_H_
 #define BASE_FUCHSIA_STARTUP_CONTEXT_H_
 
+#include <fuchsia/component/runner/cpp/fidl.h>
+#include <fuchsia/io/cpp/fidl.h>
 #include <fuchsia/sys/cpp/fidl.h>
 #include <lib/sys/cpp/component_context.h>
 #include <lib/zx/channel.h>
+
 #include <memory>
 
 #include "base/base_export.h"
 
 namespace sys {
-class ComponentContext;
 class ServiceDirectory;
 class OutgoingDirectory;
 }  // namespace sys
@@ -25,10 +27,15 @@
 // directories, resolve launch URL etc).
 // Embedders may derived from StartupContext to e.g. add bound pointers to
 // embedder-specific services, as required.
-class BASE_EXPORT StartupContext {
+class BASE_EXPORT StartupContext final {
  public:
+  explicit StartupContext(
+      ::fuchsia::component::runner::ComponentStartInfo start_info);
+  ~StartupContext();
+
+  // TODO(https://crbug.com/1065707): Remove this overload once the CFv1
+  // Runner implementations are removed.
   explicit StartupContext(::fuchsia::sys::StartupInfo startup_info);
-  virtual ~StartupContext();
 
   StartupContext(const StartupContext&) = delete;
   StartupContext& operator=(const StartupContext&) = delete;
@@ -56,11 +63,6 @@
   }
 
  private:
-  // TODO(https://crbug.com/933834): Remove these when we migrate to the new
-  // component manager APIs.
-  ::fuchsia::sys::ServiceProviderPtr additional_services_;
-  std::unique_ptr<sys::OutgoingDirectory> additional_services_directory_;
-
   std::unique_ptr<sys::ComponentContext> component_context_;
 
   // Used to store outgoing directory until ServeOutgoingDirectory() is called.
diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h
index 18510cc..4672b1e 100644
--- a/base/mac/mac_util.h
+++ b/base/mac/mac_util.h
@@ -213,6 +213,53 @@
 // Returns the serial number of the macOS device.
 BASE_EXPORT std::string GetPlatformSerialNumber();
 
+// System Settings (née System Preferences) pane or subpanes to open via
+// `OpenSystemSettingsPane()`, below. The naming is based on the naming in the
+// System Settings app in the latest macOS release, macOS 13 Ventura.
+enum class SystemSettingsPane {
+  // Accessibility > Captions
+  kAccessibility_Captions,
+
+  // Date & Time
+  kDateTime,
+
+  // Network > Proxies
+  kNetwork_Proxies,
+
+  // Printers & Scanners
+  kPrintersScanners,
+
+  // Privacy & Security > Accessibility
+  kPrivacySecurity_Accessibility,
+
+  // Privacy & Security > Bluetooth
+  // Available on macOS 11 and later.
+  kPrivacySecurity_Bluetooth,
+
+  // Privacy & Security > Camera
+  // Available on macOS 10.14 and later.
+  kPrivacySecurity_Camera,
+
+  // Privacy & Security > Extensions > Sharing
+  kPrivacySecurity_Extensions_Sharing,
+
+  // Privacy & Security > Location Services
+  kPrivacySecurity_LocationServices,
+
+  // Privacy & Security > Microphone
+  // Available on macOS 10.14 and later.
+  kPrivacySecurity_Microphone,
+
+  // Privacy & Security > Screen Recording
+  // Available on macOS 10.15 and later.
+  kPrivacySecurity_ScreenRecording,
+};
+
+// Opens the specified System Settings pane. If the specified subpane does not
+// exist on the release of macOS that is running, the parent pane will open
+// instead.
+BASE_EXPORT void OpenSystemSettingsPane(SystemSettingsPane pane);
+
 }  // namespace base::mac
 
 #endif  // BASE_MAC_MAC_UTIL_H_
diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm
index 8f001488..4ea6802 100644
--- a/base/mac/mac_util.mm
+++ b/base/mac/mac_util.mm
@@ -20,6 +20,7 @@
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_logging.h"
+#include "base/mac/scoped_aedesc.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_ioobject.h"
 #include "base/mac/scoped_nsobject.h"
@@ -485,4 +486,104 @@
   return base::SysCFStringRefToUTF8(serial_number_cfstring);
 }
 
+void OpenSystemSettingsPane(SystemSettingsPane pane) {
+  NSString* url = nil;
+  NSString* pane_file = nil;
+  NSData* subpane_data = nil;
+  switch (pane) {
+    case SystemSettingsPane::kAccessibility_Captions:
+      url = @"x-apple.systempreferences:com.apple.preference.universalaccess?"
+            @"Captioning";
+      break;
+    case SystemSettingsPane::kDateTime:
+      if (IsAtLeastOS13()) {
+        url = @"x-apple.systempreferences:com.apple.preference.datetime";
+      } else {
+        pane_file = @"/System/Library/PreferencePanes/DateAndTime.prefPane";
+      }
+      break;
+    case SystemSettingsPane::kNetwork_Proxies:
+      if (IsAtLeastOS13()) {
+        url = @"x-apple.systempreferences:com.apple.preference.network?"
+              @"Proxies";
+      } else {
+        pane_file = @"/System/Library/PreferencePanes/Network.prefPane";
+        subpane_data = [@"Proxies" dataUsingEncoding:NSASCIIStringEncoding];
+      }
+      break;
+    case SystemSettingsPane::kPrintersScanners:
+      if (IsAtLeastOS13()) {
+        url = @"x-apple.systempreferences:com.apple.preference.printfax";
+      } else {
+        pane_file = @"/System/Library/PreferencePanes/PrintAndFax.prefPane";
+      }
+      break;
+    case SystemSettingsPane::kPrivacySecurity_Accessibility:
+      url = @"x-apple.systempreferences:com.apple.preference.security?"
+            @"Privacy_Accessibility";
+      break;
+    case SystemSettingsPane::kPrivacySecurity_Bluetooth:
+      url = @"x-apple.systempreferences:com.apple.preference.security?"
+            @"Privacy_Bluetooth";
+      break;
+    case SystemSettingsPane::kPrivacySecurity_Camera:
+      url = @"x-apple.systempreferences:com.apple.preference.security?"
+            @"Privacy_Camera";
+      break;
+    case SystemSettingsPane::kPrivacySecurity_Extensions_Sharing:
+      if (IsAtLeastOS13()) {
+        // See ShareKit, -[SHKSharingServicePicker openAppExtensionsPrefpane].
+        url = @"x-apple.systempreferences:com.apple.ExtensionPreferences?"
+              @"Sharing";
+      } else {
+        // This is equivalent to the implementation of AppKit's
+        // +[NSSharingServicePicker openAppExtensionsPrefPane].
+        pane_file = @"/System/Library/PreferencePanes/Extensions.prefPane";
+        NSDictionary* subpane_dict = @{
+          @"action" : @"revealExtensionPoint",
+          @"protocol" : @"com.apple.share-services"
+        };
+        subpane_data = [NSPropertyListSerialization
+            dataWithPropertyList:subpane_dict
+                          format:NSPropertyListXMLFormat_v1_0
+                         options:0
+                           error:nil];
+      }
+      break;
+    case SystemSettingsPane::kPrivacySecurity_LocationServices:
+      url = @"x-apple.systempreferences:com.apple.preference.security?"
+            @"Privacy_LocationServices";
+      break;
+    case SystemSettingsPane::kPrivacySecurity_Microphone:
+      url = @"x-apple.systempreferences:com.apple.preference.security?"
+            @"Privacy_Microphone";
+      break;
+    case SystemSettingsPane::kPrivacySecurity_ScreenRecording:
+      url = @"x-apple.systempreferences:com.apple.preference.security?"
+            @"Privacy_ScreenCapture";
+      break;
+  }
+
+  DCHECK(url != nil ^ pane_file != nil);
+
+  if (url) {
+    [NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:url]];
+    return;
+  }
+
+  NSArray* pane_file_urls = @[ [NSURL fileURLWithPath:pane_file] ];
+
+  LSLaunchURLSpec launchSpec = {0};
+  launchSpec.itemURLs = NSToCFCast(pane_file_urls);
+  if (subpane_data) {
+    base::scoped_nsobject<NSAppleEventDescriptor> descriptor(
+        [[NSAppleEventDescriptor alloc] initWithDescriptorType:'ptru'
+                                                          data:subpane_data]);
+    launchSpec.passThruParams = descriptor.get().aeDesc;
+  }
+  launchSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
+
+  LSOpenFromURLSpec(&launchSpec, nullptr);
+}
+
 }  // namespace base::mac
diff --git a/base/task/sequence_manager/task_queue_selector.h b/base/task/sequence_manager/task_queue_selector.h
index 53c1473..7b57b3d 100644
--- a/base/task/sequence_manager/task_queue_selector.h
+++ b/base/task/sequence_manager/task_queue_selector.h
@@ -16,7 +16,6 @@
 #include "base/task/sequence_manager/sequence_manager.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
 #include "base/task/sequence_manager/task_order.h"
-#include "base/task/sequence_manager/task_queue_selector_logic.h"
 #include "base/task/sequence_manager/work_queue_sets.h"
 #include "base/values.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/base/task/sequence_manager/task_queue_selector_logic.h b/base/task/sequence_manager/task_queue_selector_logic.h
deleted file mode 100644
index 46e62d1..0000000
--- a/base/task/sequence_manager/task_queue_selector_logic.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_
-#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_
-
-namespace base {
-namespace sequence_manager {
-namespace internal {
-
-// Used to describe the logic trigerred when a task queue is selected to
-// service.
-// This enum is used for histograms and should not be renumbered.
-enum class TaskQueueSelectorLogic {
-  // Selected due to priority rules.
-  kControlPriorityLogic = 0,
-  kHighestPriorityLogic = 1,
-  kHighPriorityLogic = 2,
-  kNormalPriorityLogic = 3,
-  kLowPriorityLogic = 4,
-  kBestEffortPriorityLogic = 5,
-
-  // Selected due to starvation logic.
-  kHighPriorityStarvationLogic = 6,
-  kNormalPriorityStarvationLogic = 7,
-  kLowPriorityStarvationLogic = 8,
-
-  kCount = 9,
-};
-
-}  // namespace internal
-}  // namespace sequence_manager
-}  // namespace base
-
-#endif  // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_
diff --git a/build/android/gyp/create_java_binary_script.py b/build/android/gyp/create_java_binary_script.py
index 2c7ad27e..a2f701e 100755
--- a/build/android/gyp/create_java_binary_script.py
+++ b/build/android/gyp/create_java_binary_script.py
@@ -82,15 +82,17 @@
       help='Name of the java class with the "main" entry point.')
   parser.add_option('--classpath', action='append', default=[],
       help='Classpath for running the jar.')
-  parser.add_option('--noverify', action='store_true',
-      help='JVM flag: noverify.')
+  parser.add_option('--max-heap-size', default='1G', help='Argument for -Xmx')
+  parser.add_option('--noverify',
+                    action='store_true',
+                    help='JVM flag: noverify.')
   parser.add_option('--tiered-stop-at-level-one',
                     action='store_true',
                     help='JVM flag: -XX:TieredStopAtLevel=1.')
 
   options, extra_program_args = parser.parse_args(argv)
 
-  extra_flags = []
+  extra_flags = [f'java_cmd.append("-Xmx{options.max_heap_size}")']
   if options.noverify:
     extra_flags.append('java_cmd.append("-noverify")')
   if options.tiered_stop_at_level_one:
diff --git a/build/config/fuchsia/test/README.md b/build/config/fuchsia/test/README.md
index 009cc2d..f2084d9 100644
--- a/build/config/fuchsia/test/README.md
+++ b/build/config/fuchsia/test/README.md
@@ -40,7 +40,14 @@
 For tests that test logging functionality by providing `fuchsia.logger.Log`.
 
 #### test_ui_stack.shard.test-cml
-For tests that need an isolated Scenic by way of Fuchsia's test-ui-stack.
+For tests that need an isolated UI subsystem, that supports the Flatland
+API set.  This allows tests to e.g. run with view-focus unaffected by any
+other tests running concurrently on the device, as well as providing test-only
+functionality such as input-injection support.
+
+#### gfx_test_ui_stack.shard.test-cml
+For tests that need an isolated display subsystem supporting the legacy
+Scenic/GFX APIs.
 
 ### WebEngine Fragments
 The following fragments are specific to WebEngine functionality as documented
diff --git a/build/config/fuchsia/test/gfx_test_ui_stack.shard.test-cml b/build/config/fuchsia/test/gfx_test_ui_stack.shard.test-cml
new file mode 100644
index 0000000..e9c5067a
--- /dev/null
+++ b/build/config/fuchsia/test/gfx_test_ui_stack.shard.test-cml
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Used in tests which are hard-coded for the Scenic/GFX API-set.
+// Use test_ui_stack.shard.test-cml when tetsing for Flatland, or when the
+// choice of API-set is not important.
+{
+  children: [
+    {
+      name: "test_ui_stack",
+      url: "fuchsia-pkg://fuchsia.com/gfx-scene-manager-test-ui-stack#meta/test-ui-stack.cm",
+    },
+  ],
+  offer: [
+    {
+      protocol: [
+        "fuchsia.logger.LogSink",
+        "fuchsia.scheduler.ProfileProvider",
+        "fuchsia.sysmem.Allocator",
+        "fuchsia.tracing.provider.Registry",
+        "fuchsia.vulkan.loader.Loader",
+      ],
+      from: "parent",
+      to: "#test_ui_stack",
+    },
+  ],
+  use: [
+    {
+      protocol: [
+        "fuchsia.accessibility.semantics.SemanticsManager",
+        "fuchsia.element.GraphicalPresenter",
+        "fuchsia.ui.composition.Allocator",
+        "fuchsia.ui.composition.Flatland",
+        "fuchsia.ui.input3.Keyboard",
+        "fuchsia.ui.scenic.Scenic",
+      ],
+      from: "#test_ui_stack",
+    },
+  ],
+}
diff --git a/build/config/fuchsia/test/test_ui_stack.shard.test-cml b/build/config/fuchsia/test/test_ui_stack.shard.test-cml
index a57d434..3ee8563 100644
--- a/build/config/fuchsia/test/test_ui_stack.shard.test-cml
+++ b/build/config/fuchsia/test/test_ui_stack.shard.test-cml
@@ -25,6 +25,7 @@
     {
       protocol: [
         "fuchsia.accessibility.semantics.SemanticsManager",
+        "fuchsia.element.GraphicalPresenter",
         "fuchsia.ui.composition.Allocator",
         "fuchsia.ui.composition.Flatland",
         "fuchsia.ui.input3.Keyboard",
diff --git a/build/linux/unbundle/openh264.gn b/build/linux/unbundle/openh264.gn
index 1a19ceed..f4abd9b 100644
--- a/build/linux/unbundle/openh264.gn
+++ b/build/linux/unbundle/openh264.gn
@@ -11,7 +11,7 @@
 
 shim_headers("openh264_shim") {
   prefix = "wels/"
-  root_path = "src/codec/api/svc"
+  root_path = "src/codec/api/wels"
   headers = [
     "codec_api.h",
     "codec_app_def.h",
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
index e650c6b2..3946a919 100644
--- a/cc/base/math_util.cc
+++ b/cc/base/math_util.cc
@@ -330,11 +330,9 @@
 gfx::QuadF MathUtil::InverseMapQuadToLocalSpace(
     const gfx::Transform& device_transform,
     const gfx::QuadF& device_quad) {
-  gfx::Transform inverse_device_transform(gfx::Transform::kSkipInitialization);
-  DCHECK(device_transform.IsInvertible());
   DCHECK(device_transform.IsFlat());
-  bool did_invert = device_transform.GetInverse(&inverse_device_transform);
-  DCHECK(did_invert);
+  gfx::Transform inverse_device_transform =
+      device_transform.GetCheckedInverse();
   bool clipped = false;
   gfx::QuadF local_quad =
       MathUtil::MapQuad(inverse_device_transform, device_quad, &clipped);
diff --git a/cc/benchmarks/invalidation_benchmark.cc b/cc/benchmarks/invalidation_benchmark.cc
index 02d9470..a2ef2c6 100644
--- a/cc/benchmarks/invalidation_benchmark.cc
+++ b/cc/benchmarks/invalidation_benchmark.cc
@@ -70,10 +70,8 @@
 
 void InvalidationBenchmark::RunOnLayer(PictureLayer* layer) {
   gfx::Rect visible_layer_rect = gfx::Rect(layer->bounds());
-  gfx::Transform from_screen;
-  bool invertible = layer->ScreenSpaceTransform().GetInverse(&from_screen);
-  if (!invertible)
-    from_screen = gfx::Transform();
+  gfx::Transform from_screen =
+      layer->ScreenSpaceTransform().InverseOrIdentity();
   gfx::Rect viewport_rect = MathUtil::ProjectEnclosingClippedRect(
       from_screen, layer->layer_tree_host()->device_viewport_rect());
   visible_layer_rect.Intersect(viewport_rect);
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc
index 929c396..3ebce36 100644
--- a/cc/input/input_handler.cc
+++ b/cc/input/input_handler.cc
@@ -854,14 +854,11 @@
   if (out_touch_action) {
     gfx::Transform layer_screen_space_transform =
         layer_impl_with_touch_handler->ScreenSpaceTransform();
-    gfx::Transform inverse_layer_screen_space(
-        gfx::Transform::kSkipInitialization);
-    bool can_be_inversed =
-        layer_screen_space_transform.GetInverse(&inverse_layer_screen_space);
     // Getting here indicates that |layer_impl_with_touch_handler| is non-null,
     // which means that the |hit| in FindClosestMatchingLayer() is true, which
     // indicates that the inverse is available.
-    DCHECK(can_be_inversed);
+    gfx::Transform inverse_layer_screen_space =
+        layer_screen_space_transform.GetCheckedInverse();
     bool clipped = false;
     gfx::Point3F planar_point = MathUtil::ProjectPoint3D(
         inverse_layer_screen_space, device_viewport_point, &clipped);
@@ -1667,14 +1664,10 @@
   // the scroll hit test in the first place.
   const gfx::Transform screen_space_transform =
       GetScrollTree().ScreenSpaceTransform(scroll_node.id);
-  DCHECK(screen_space_transform.IsInvertible());
-  gfx::Transform inverse_screen_space_transform(
-      gfx::Transform::kSkipInitialization);
-  bool did_invert =
-      screen_space_transform.GetInverse(&inverse_screen_space_transform);
   // TODO(shawnsingh): With the advent of impl-side scrolling for non-root
   // layers, we may need to explicitly handle uninvertible transforms here.
-  DCHECK(did_invert);
+  gfx::Transform inverse_screen_space_transform =
+      screen_space_transform.GetCheckedInverse();
 
   float scale_from_viewport_to_screen_space =
       compositor_delegate_.DeviceScaleFactor();
diff --git a/cc/input/scrollbar_controller.cc b/cc/input/scrollbar_controller.cc
index e47ff01..5a00962b 100644
--- a/cc/input/scrollbar_controller.cc
+++ b/cc/input/scrollbar_controller.cc
@@ -786,8 +786,7 @@
     return gfx::PointF(0, 0);
   }
 
-  gfx::Transform inverse_screen_space_transform(
-      gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_screen_space_transform;
 
   gfx::Transform scaled_screen_space_transform(
       scrollbar->ScreenSpaceTransform());
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 1354c0e4..6bd2ae13 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -932,7 +932,7 @@
     return true;
   }
 
-  gfx::Transform inverse(gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse;
   if (b.GetInverse(&inverse)) {
     inverse *= a;
     return inverse.Preserves2dAxisAlignment();
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index c11493b..d977f656 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -684,7 +684,7 @@
   if (visible_rect_in_content_space.IsEmpty() ||
       layer_tree_impl()->GetDeviceViewport() !=
           viewport_rect_for_tile_priority) {
-    gfx::Transform view_to_layer(gfx::Transform::kSkipInitialization);
+    gfx::Transform view_to_layer;
     if (ScreenSpaceTransform().GetInverse(&view_to_layer)) {
       // Transform from view space to content space.
       visible_rect_in_content_space = MathUtil::ProjectEnclosingClippedRect(
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index e7cbb0025..2e3bee3 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -240,7 +240,7 @@
   // Calculate projection from the target surface rect to local
   // space. Non-invertible draw transforms means no able to bring clipped rect
   // in target space back to local space, early out without clip.
-  gfx::Transform target_to_surface(gfx::Transform::kSkipInitialization);
+  gfx::Transform target_to_surface;
   if (!draw_transform().GetInverse(&target_to_surface))
     return accumulated_content_rect();
 
diff --git a/cc/trees/draw_properties_unittest.cc b/cc/trees/draw_properties_unittest.cc
index 7c12831a..fd3b0d4 100644
--- a/cc/trees/draw_properties_unittest.cc
+++ b/cc/trees/draw_properties_unittest.cc
@@ -286,7 +286,7 @@
   gfx::Transform translation_to_anchor;
   translation_to_anchor.Translate(5.0, 0.0);
   gfx::Transform expected_result = translation_to_anchor * layer_transform *
-                                   gfx::InvertAndCheck(translation_to_anchor);
+                                   translation_to_anchor.GetCheckedInverse();
   SetTransformOrigin(layer, gfx::Point3F(5.f, 0.f, 0.f));
   UpdateActiveTreeDrawProperties();
   EXPECT_TRANSFORM_EQ(expected_result, draw_property_utils::DrawTransform(
@@ -299,8 +299,7 @@
   // current implementation of CalculateDrawProperties does this implicitly, but
   // it is still worth testing to detect accidental regressions.
   expected_result = position_transform * translation_to_anchor *
-                    layer_transform *
-                    gfx::InvertAndCheck(translation_to_anchor);
+                    layer_transform * translation_to_anchor.GetCheckedInverse();
   SetPostTranslation(layer, gfx::Vector2dF(0.f, 1.2f));
   UpdateActiveTreeDrawProperties();
   EXPECT_TRANSFORM_EQ(expected_result, draw_property_utils::DrawTransform(
@@ -454,7 +453,7 @@
   parent_translation_to_anchor.Translate(2.5, 3.0);
   gfx::Transform parent_composite_transform =
       parent_translation_to_anchor * parent_layer_transform *
-      gfx::InvertAndCheck(parent_translation_to_anchor);
+      parent_translation_to_anchor.GetCheckedInverse();
   SetTransform(parent, parent_layer_transform);
   SetPostTranslation(parent, gfx::Vector2dF());
   UpdateActiveTreeDrawProperties();
@@ -485,7 +484,7 @@
 
   gfx::Transform parent_composite_transform =
       parent_translation_to_anchor * parent_layer_transform *
-      gfx::InvertAndCheck(parent_translation_to_anchor);
+      parent_translation_to_anchor.GetCheckedInverse();
   gfx::Vector2dF parent_composite_scale =
       gfx::ComputeTransform2dScaleComponents(parent_composite_transform, 1.f);
   gfx::Transform surface_sublayer_transform;
@@ -493,7 +492,7 @@
                                    parent_composite_scale.y());
   gfx::Transform surface_sublayer_composite_transform =
       parent_composite_transform *
-      gfx::InvertAndCheck(surface_sublayer_transform);
+      surface_sublayer_transform.GetCheckedInverse();
 
   root->SetBounds(gfx::Size(1, 2));
   parent->SetBounds(gfx::Size(100, 120));
@@ -573,7 +572,7 @@
   layer_transform.Translate(1.0, 1.0);
 
   gfx::Transform A = translation_to_anchor * layer_transform *
-                     gfx::InvertAndCheck(translation_to_anchor);
+                     translation_to_anchor.GetCheckedInverse();
 
   gfx::Vector2dF surface1_parent_transform_scale =
       gfx::ComputeTransform2dScaleComponents(A, 1.f);
@@ -585,7 +584,7 @@
   gfx::Transform SS1 = surface1_sublayer_transform;
   // S1 = transform to move from render_surface1 pixels to the layer space of
   // the owning layer
-  gfx::Transform S1 = gfx::InvertAndCheck(surface1_sublayer_transform);
+  gfx::Transform S1 = surface1_sublayer_transform.GetCheckedInverse();
 
   gfx::Vector2dF surface2_parent_transform_scale =
       gfx::ComputeTransform2dScaleComponents(SS1 * A, 1.f);
@@ -597,7 +596,7 @@
   gfx::Transform SS2 = surface2_sublayer_transform;
   // S2 = transform to move from render_surface2 pixels to the layer space of
   // the owning layer
-  gfx::Transform S2 = gfx::InvertAndCheck(surface2_sublayer_transform);
+  gfx::Transform S2 = surface2_sublayer_transform.GetCheckedInverse();
 
   root->SetBounds(gfx::Size(1, 2));
   parent->SetBounds(gfx::Size(10, 10));
@@ -2034,7 +2033,7 @@
 
 static bool ProjectionClips(const gfx::Transform& map_transform,
                             const gfx::RectF& mapped_rect) {
-  gfx::Transform inverse(gfx::InvertAndCheck(map_transform));
+  gfx::Transform inverse = map_transform.GetCheckedInverse();
   bool clipped = false;
   if (!clipped)
     MathUtil::ProjectPoint(inverse, mapped_rect.top_right(), &clipped);
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 731b471..1de624f 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3169,10 +3169,9 @@
                                   SK_Scalar1 / device_scale_factor);
   surface_to_root_transform.FlattenTo2d();
   // TODO(sunxd): Avoid losing precision by not using inverse if possible.
-  [[maybe_unused]] bool ok =
-      surface_to_root_transform.GetInverse(&hit_test_region->transform);
-  // Note: If |ok| is false, the |transform| is set to the identity before
-  // returning, which is what we want.
+  // Note: |transform| is set to the identity if |surface_to_root_transform| is
+  // not invertible, which is what we want.
+  hit_test_region->transform = surface_to_root_transform.InverseOrIdentity();
 }
 
 absl::optional<viz::HitTestRegionList> LayerTreeHostImpl::BuildHitTestData() {
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 9081bde..3d199b4 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -2237,8 +2237,7 @@
     float* distance_to_camera) {
   // If the transform is not invertible, then assume that this point doesn't hit
   // this rect.
-  gfx::Transform inverse_local_space_to_screen_space(
-      gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_local_space_to_screen_space;
   if (!local_space_to_screen_space_transform.GetInverse(
           &inverse_local_space_to_screen_space))
     return false;
@@ -2324,8 +2323,7 @@
 
   // If the transform is not invertible, then assume that this point doesn't hit
   // this region.
-  gfx::Transform inverse_screen_space_transform(
-      gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_screen_space_transform;
   if (!screen_space_transform.GetInverse(&inverse_screen_space_transform))
     return false;
 
diff --git a/cc/trees/occlusion.cc b/cc/trees/occlusion.cc
index a0664c7e..5ced731 100644
--- a/cc/trees/occlusion.cc
+++ b/cc/trees/occlusion.cc
@@ -56,7 +56,7 @@
   if (unoccluded_rect_in_target_surface.IsEmpty())
     return gfx::Rect();
 
-  gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_draw_transform;
   bool ok = draw_transform_.GetInverse(&inverse_draw_transform);
   // TODO(ajuma): Skip drawing layers with uninvertible draw transforms, and
   // change this to a DCHECK. crbug.com/517170
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index fdb210cf..d3248926 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -81,8 +81,7 @@
 static gfx::Rect ScreenSpaceClipRectInTargetSurface(
     const RenderSurfaceImpl* target_surface,
     const gfx::Rect& screen_space_clip_rect) {
-  gfx::Transform inverse_screen_space_transform(
-      gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_screen_space_transform;
   if (!target_surface->screen_space_transform().GetInverse(
           &inverse_screen_space_transform))
     return target_surface->content_rect();
@@ -147,9 +146,7 @@
       new_occlusion_immune_ancestor &&
       new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
 
-  gfx::Transform inverse_new_target_screen_space_transform(
-      // Note carefully, not used if screen space transform is uninvertible.
-      gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_new_target_screen_space_transform;
   bool have_transform_from_screen_to_new_target =
       new_target_surface->screen_space_transform().GetInverse(
           &inverse_new_target_screen_space_transform);
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 4a30a227..1142eb7 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -696,9 +696,7 @@
 
   gfx::Transform root_to_screen;
   root_to_screen.Scale(screen_space_scale.x(), screen_space_scale.y());
-  gfx::Transform root_from_screen;
-  bool invertible = root_to_screen.GetInverse(&root_from_screen);
-  DCHECK(invertible);
+  gfx::Transform root_from_screen = root_to_screen.GetCheckedInverse();
   if (root_to_screen != ToScreen(kRootPropertyNodeId)) {
     SetToScreen(kRootPropertyNodeId, root_to_screen);
     SetFromScreen(kRootPropertyNodeId, root_from_screen);
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index e690852..0024ba3 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -401,6 +401,7 @@
 }
 
 void ProxyImpl::OnHungCommit() {
+  UMA_HISTOGRAM_BOOLEAN("Compositing.Renderer.CommitHung", true);
   static auto* hung_commit_data = base::debug::AllocateCrashKeyString(
       "hung_commit", base::debug::CrashKeySize::Size256);
   std::string debug_info = scheduler_->GetHungCommitDebugInfo();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 2c317494..7f874085 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -1697,17 +1697,24 @@
         // focus dependency is because doing it earlier can cause drawing bugs, e.g. crbug/673831.
         if (!mNativeInitialized || !hasWindowFocus()) return;
 
-        // The window background color is used as the resizing background color in Android N+
-        // multi-window mode. See crbug.com/602366.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+        if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(this)
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             changeBackgroundColorForResizing();
         } else {
-            // Post the removeWindowBackground() call as a separate task, as doing it synchronously
-            // here can cause redrawing glitches. See crbug.com/686662 for an example problem.
+            // Post the background update call as a separate task, as doing it synchronously
+            // here can cause redrawing glitches. See crbug.com/686662 and crbug.com/1260127 for
+            // example problems.
             Handler handler = new Handler();
-            handler.post(() -> removeWindowBackground());
+            handler.post(() -> {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                    // The window background color is used as the resizing background color in
+                    // Android N+ multi-window mode. See crbug.com/602366.
+                    changeBackgroundColorForResizing();
+                } else {
+                    removeWindowBackground();
+                }
+            });
         }
-
         mRemoveWindowBackgroundDone = true;
     }
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 00a2779..03bed95 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -6200,7 +6200,7 @@
     allow_circular_includes_from += [ "//chrome/browser/apps/app_shim" ]
     sources += [
       "accessibility/caption_settings_dialog.h",
-      "accessibility/caption_settings_dialog_mac.mm",
+      "accessibility/caption_settings_dialog_mac.cc",
       "app_controller_mac.h",
       "app_controller_mac.mm",
       "apps/intent_helper/mac_intent_picker_helpers.h",
@@ -6426,6 +6426,8 @@
       "enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h",
       "enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.cc",
       "enterprise/connectors/device_trust/signals/decorators/browser/browser_signals_decorator.h",
+      "enterprise/idle/browser_closer.cc",
+      "enterprise/idle/browser_closer.h",
       "enterprise/idle/idle_service.cc",
       "enterprise/idle/idle_service.h",
       "enterprise/idle/idle_service_factory.cc",
@@ -6906,7 +6908,7 @@
       ]
     }
     if (is_mac) {
-      sources += [ "printing/printer_manager_dialog_mac.mm" ]
+      sources += [ "printing/printer_manager_dialog_mac.cc" ]
     }
     if (is_chromeos_lacros) {
       sources += [
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9cce2449..7320e545 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2412,14 +2412,6 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
-const FeatureEntry::FeatureParam kLensOnQuickActionSearchWidgetOnTablet[] = {
-    {"enableCameraAssistedSearchOnTabletWidget", "true"}};
-
-const FeatureEntry::FeatureVariation
-    kLensOnQuickActionSearchWidgetVariations[] = {
-        {"(on Tablet)", kLensOnQuickActionSearchWidgetOnTablet,
-         std::size(kLensOnQuickActionSearchWidgetOnTablet), nullptr}};
-
 const FeatureEntry::FeatureParam kLensCameraAssistedSearchLensButtonStart[] = {
     {"searchBoxStartVariantForLensCameraAssistedSearch", "true"}};
 
@@ -7419,14 +7411,6 @@
                                     kLensCameraAssistedSearchVariations,
                                     "LensCameraAssistedSearch")},
 
-    {"lens-on-quick-action-search-widget",
-     flag_descriptions::kLensOnQuickActionSearchWidgetName,
-     flag_descriptions::kLensOnQuickActionSearchWidgetDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         chrome::android::kLensOnQuickActionSearchWidget,
-         kLensOnQuickActionSearchWidgetVariations,
-         "LensOnQuickActionSearchWidget")},
-
     {"enable-iph", flag_descriptions::kEnableIphName,
      flag_descriptions::kEnableIphDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(feature_engagement::kEnableIPH)},
@@ -8378,11 +8362,6 @@
      kOsCrOS | kOsLacros,
      FEATURE_VALUE_TYPE(features::kGetDisplayMediaSetAutoSelectAllScreens)},
 
-    {"enable-vaapi-av1-decode-acceleration",
-     flag_descriptions::kVaapiAV1DecoderName,
-     flag_descriptions::kVaapiAV1DecoderDescription, kOsCrOS | kOsLacros,
-     FEATURE_VALUE_TYPE(media::kVaapiAV1Decoder)},
-
     {"default-chrome-apps-migration",
      flag_descriptions::kDefaultChromeAppsMigrationName,
      flag_descriptions::kDefaultChromeAppsMigrationDescription, kOsCrOS,
@@ -8823,12 +8802,6 @@
      FEATURE_VALUE_TYPE(ash::features::kPhoneHubFeatureSetupErrorHandling)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-    {"sameparty-cookies-considered-first-party",
-     flag_descriptions::kSamePartyCookiesConsideredFirstPartyName,
-     flag_descriptions::kSamePartyCookiesConsideredFirstPartyDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(net::features::kSamePartyCookiesConsideredFirstParty)},
-
     {"partitioned-cookies", flag_descriptions::kPartitionedCookiesName,
      flag_descriptions::kPartitionedCookiesDescription, kOsAll,
      FEATURE_VALUE_TYPE(net::features::kPartitionedCookies)},
diff --git a/chrome/browser/accessibility/caption_settings_dialog_mac.cc b/chrome/browser/accessibility/caption_settings_dialog_mac.cc
new file mode 100644
index 0000000..2181177
--- /dev/null
+++ b/chrome/browser/accessibility/caption_settings_dialog_mac.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/accessibility/caption_settings_dialog.h"
+
+#include "base/mac/mac_util.h"
+
+namespace captions {
+
+void CaptionSettingsDialog::ShowCaptionSettingsDialog() {
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kAccessibility_Captions);
+}
+
+}  // namespace captions
diff --git a/chrome/browser/accessibility/caption_settings_dialog_mac.mm b/chrome/browser/accessibility/caption_settings_dialog_mac.mm
deleted file mode 100644
index 0213b08..0000000
--- a/chrome/browser/accessibility/caption_settings_dialog_mac.mm
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/accessibility/caption_settings_dialog.h"
-
-#import <AppKit/AppKit.h>
-
-namespace captions {
-
-static NSString* kCaptionSettingsUrlString =
-    @"x-apple.systempreferences:com.apple.preference.universalaccess?"
-    @"Captioning";
-
-void CaptionSettingsDialog::ShowCaptionSettingsDialog() {
-  [[NSWorkspace sharedWorkspace]
-      openURL:[NSURL URLWithString:kCaptionSettingsUrlString]];
-}
-
-}  // namespace captions
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index e30537e..f615451 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/extensions/api/permissions/permissions_api.h"
 #include "chrome/browser/extensions/component_loader.h"
@@ -957,9 +956,9 @@
     ASSERT_FALSE(GetFirstAppWindow());
 
     // Relaunch the app and get a new AppWindow.
-    content::WindowedNotificationObserver app_loaded_observer(
-        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-        content::NotificationService::AllSources());
+    content::CreateAndLoadWebContentsObserver app_loaded_observer(
+        /*num_expected_contents=*/2);
+
     apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
         ->BrowserAppLauncher()
         ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
@@ -1098,9 +1097,7 @@
   // Ensure that we wait until the background page is run (to register the
   // OnLaunched listener) before trying to open the application. This is similar
   // to LoadAndLaunchPlatformApp, but we want to load as a component extension.
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
+  content::CreateAndLoadWebContentsObserver app_loaded_observer;
 
   const Extension* extension = LoadExtensionAsComponent(
       test_data_dir_.AppendASCII("platform_apps").AppendASCII("component"));
@@ -1162,9 +1159,7 @@
 IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, ComponentAppBackgroundPage) {
   CheckExtensionInstalledObserver should_install(browser()->profile());
   // Since we are forcing an upgrade, we need to wait for the load again.
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
+  content::CreateAndLoadWebContentsObserver app_loaded_observer;
 
   const Extension* extension = LoadExtensionAsComponent(
       test_data_dir_.AppendASCII("platform_apps").AppendASCII("component"));
@@ -1188,9 +1183,7 @@
   // Ensure that we wait until the background page is run (to register the
   // OnLaunched listener) before trying to open the application. This is similar
   // to LoadAndLaunchPlatformApp, but we want to load as a component extension.
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
+  content::CreateAndLoadWebContentsObserver app_loaded_observer;
 
   const Extension* extension = LoadExtensionAsComponent(
       test_data_dir_.AppendASCII("platform_apps").AppendASCII("component"));
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc
index 47826b1..6a5437e 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc
@@ -312,8 +312,6 @@
   void SetUpOnMainThread() override {
     SpokenFeedbackAppListBaseTest::SetUpOnMainThread();
 
-    Shell::Get()->app_list_controller()->MarkSuggestedContentInfoDismissed();
-
     AppListClientImpl* app_list_client = AppListClientImpl::GetInstance();
 
     // Reset default search controller, so the test has better control over the
diff --git a/chrome/browser/ash/crosapi/local_printer_ash.cc b/chrome/browser/ash/crosapi/local_printer_ash.cc
index 72de4ff1..76d152e 100644
--- a/chrome/browser/ash/crosapi/local_printer_ash.cc
+++ b/chrome/browser/ash/crosapi/local_printer_ash.cc
@@ -123,6 +123,26 @@
           .Then(std::move(callback)));
 }
 
+void OnOAuthAccessTokenObtained(
+    std::unique_ptr<ash::printing::PrinterAuthenticator> /* authenticator */,
+    mojom::LocalPrinter::GetOAuthAccessTokenCallback callback,
+    ash::printing::oauth2::StatusCode status,
+    std::string access_token) {
+  if (status != ash::printing::oauth2::StatusCode::kOK) {
+    // An error occurred.
+    std::move(callback).Run(
+        mojom::GetOAuthAccessTokenResult::NewError(mojom::OAuthError::New()));
+    return;
+  }
+  if (access_token.empty()) {
+    std::move(callback).Run(mojom::GetOAuthAccessTokenResult::NewNone(
+        mojom::OAuthNotNeeded::New()));
+  } else {
+    std::move(callback).Run(mojom::GetOAuthAccessTokenResult::NewToken(
+        mojom::OAuthAccessToken::New(std::move(access_token))));
+  }
+}
+
 }  // namespace
 
 LocalPrinterAsh::LocalPrinterAsh()
@@ -569,6 +589,39 @@
   std::move(callback).Run();
 }
 
+void LocalPrinterAsh::GetOAuthAccessToken(
+    const std::string& printer_id,
+    GetOAuthAccessTokenCallback callback) {
+  if (!chromeos::features::IsOAuthIppEnabled()) {
+    std::move(callback).Run(mojom::GetOAuthAccessTokenResult::NewNone(
+        mojom::OAuthNotNeeded::New()));
+    return;
+  }
+  Profile* profile = GetProfile();
+  DCHECK(profile);
+  ash::CupsPrintersManager* printers_manager =
+      ash::CupsPrintersManagerFactory::GetForBrowserContext(profile);
+  DCHECK(printers_manager);
+  absl::optional<chromeos::Printer> printer =
+      printers_manager->GetPrinter(printer_id);
+  if (!printer) {
+    // If the printer was removed, the lookup will fail.
+    std::move(callback).Run(
+        mojom::GetOAuthAccessTokenResult::NewError(mojom::OAuthError::New()));
+    return;
+  }
+  ash::printing::oauth2::AuthorizationZonesManager* auth_manager =
+      ash::printing::oauth2::AuthorizationZonesManagerFactory::
+          GetForBrowserContext(profile);
+  DCHECK(auth_manager);
+  auto authenticator = std::make_unique<ash::printing::PrinterAuthenticator>(
+      printers_manager, auth_manager, *printer);
+  ash::printing::PrinterAuthenticator* authenticator_ptr = authenticator.get();
+  authenticator_ptr->ObtainAccessTokenIfNeeded(
+      base::BindOnce(OnOAuthAccessTokenObtained, std::move(authenticator),
+                     std::move(callback)));
+}
+
 scoped_refptr<chromeos::PpdProvider> LocalPrinterAsh::CreatePpdProvider(
     Profile* profile) {
   return ash::CreatePpdProvider(profile);
diff --git a/chrome/browser/ash/crosapi/local_printer_ash.h b/chrome/browser/ash/crosapi/local_printer_ash.h
index f6e40cb..28cc070 100644
--- a/chrome/browser/ash/crosapi/local_printer_ash.h
+++ b/chrome/browser/ash/crosapi/local_printer_ash.h
@@ -116,6 +116,8 @@
   void AddPrintJobObserver(mojo::PendingRemote<mojom::PrintJobObserver> remote,
                            mojom::PrintJobSource source,
                            AddPrintJobObserverCallback callback) override;
+  void GetOAuthAccessToken(const std::string& printer_id,
+                           GetOAuthAccessTokenCallback callback) override;
 
  private:
   void NotifyPrintJobUpdate(base::WeakPtr<ash::CupsPrintJob> job,
diff --git a/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc b/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc
index e456083..0216749 100644
--- a/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/local_printer_ash_unittest.cc
@@ -11,14 +11,20 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
+#include "base/functional/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/ash/crosapi/test_local_printer_ash.h"
 #include "chrome/browser/ash/printing/cups_printers_manager_factory.h"
+#include "chrome/browser/ash/printing/oauth2/authorization_zones_manager_factory.h"
+#include "chrome/browser/ash/printing/oauth2/mock_authorization_zones_manager.h"
+#include "chrome/browser/ash/printing/oauth2/status_code.h"
 #include "chrome/browser/ash/printing/test_cups_printers_manager.h"
 #include "chrome/browser/ash/printing/test_printer_configurer.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -42,6 +48,7 @@
 #include "printing/backend/test_print_backend.h"
 #include "printing/buildflags/buildflags.h"
 #include "printing/printing_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/size.h"
@@ -82,6 +89,14 @@
   fetched_eula_url = eula_url;
 }
 
+void RecordOAuthAccessToken(
+    crosapi::mojom::GetOAuthAccessTokenResultPtr& out,
+    base::OnceClosure closure,
+    crosapi::mojom::GetOAuthAccessTokenResultPtr param) {
+  out = std::move(param);
+  std::move(closure).Run();
+}
+
 Printer CreateTestPrinter(const std::string& id,
                           const std::string& name,
                           const std::string& description) {
@@ -998,4 +1013,169 @@
             mojom->status_reasons[0]->severity);
 }
 
+// Base testing class for `LocalPrinterAsh` with enabled OAuth2 feature.
+class LocalPrinterAshWithOAuth2Test : public testing::Test {
+ public:
+  const std::vector<PrinterSemanticCapsAndDefaults::Paper> kPapers = {
+      {"bar", "vendor", {600, 600}}};
+
+  LocalPrinterAshWithOAuth2Test() = default;
+  LocalPrinterAshWithOAuth2Test(const LocalPrinterAshWithOAuth2Test&) = delete;
+  LocalPrinterAshWithOAuth2Test& operator=(
+      const LocalPrinterAshWithOAuth2Test&) = delete;
+  ~LocalPrinterAshWithOAuth2Test() override = default;
+
+  sync_preferences::TestingPrefServiceSyncable* GetPrefs() {
+    return profile_.GetTestingPrefService();
+  }
+
+  void SetUp() override {
+    ppd_provider_ = base::MakeRefCounted<FakePpdProvider>();
+    ash::CupsPrintersManagerFactory::GetInstance()->SetTestingFactoryAndUse(
+        &profile_,
+        base::BindLambdaForTesting([this](content::BrowserContext* context)
+                                       -> std::unique_ptr<KeyedService> {
+          auto printers_manager =
+              std::make_unique<ash::TestCupsPrintersManager>();
+          printers_manager_ = printers_manager.get();
+          return printers_manager;
+        }));
+    ash::printing::oauth2::AuthorizationZonesManagerFactory::GetInstance()
+        ->SetTestingFactoryAndUse(
+            &profile_,
+            base::BindLambdaForTesting([this](content::BrowserContext* context)
+                                           -> std::unique_ptr<KeyedService> {
+              auto auth_manager = std::make_unique<testing::StrictMock<
+                  ash::printing::oauth2::MockAuthorizationZoneManager>>();
+              auth_manager_ = auth_manager.get();
+              return auth_manager;
+            }));
+    local_printer_ash_ =
+        std::make_unique<TestLocalPrinterAsh>(&profile_, ppd_provider_);
+  }
+
+ protected:
+  ash::TestCupsPrintersManager& printers_manager() {
+    DCHECK(printers_manager_);
+    return *printers_manager_;
+  }
+
+  ash::printing::oauth2::MockAuthorizationZoneManager& auth_manager() {
+    DCHECK(auth_manager_);
+    return *auth_manager_;
+  }
+
+  crosapi::LocalPrinterAsh* local_printer_ash() {
+    return local_printer_ash_.get();
+  }
+
+ private:
+  // Enables the OAuth2 feature.
+  base::test::ScopedFeatureList feature_list_ =
+      base::test::ScopedFeatureList(::ash::features::kEnableOAuthIpp);
+  // Must outlive `profile_`.
+  content::BrowserTaskEnvironment task_environment_;
+
+  // Must outlive `printers_manager_`.
+  TestingProfile profile_;
+  ash::TestCupsPrintersManager* printers_manager_ = nullptr;
+  testing::StrictMock<ash::printing::oauth2::MockAuthorizationZoneManager>*
+      auth_manager_ = nullptr;
+  scoped_refptr<FakePpdProvider> ppd_provider_;
+  std::unique_ptr<crosapi::LocalPrinterAsh> local_printer_ash_;
+};
+
+TEST_F(LocalPrinterAshWithOAuth2Test, GetOAuthAccessTokenUnknownPrinter) {
+  crosapi::mojom::GetOAuthAccessTokenResultPtr result;
+  base::RunLoop loop;
+  local_printer_ash()->GetOAuthAccessToken(
+      "printer_id", base::BindOnce(&RecordOAuthAccessToken, std::ref(result),
+                                   loop.QuitClosure()));
+  loop.Run();
+
+  ASSERT_TRUE(result);
+  EXPECT_TRUE(result->is_error());
+}
+
+TEST_F(LocalPrinterAshWithOAuth2Test, GetOAuthAccessTokenNonOAuthPrinter) {
+  Printer saved_printer =
+      CreateTestPrinter("printer_id", "saved", "description1");
+  printers_manager().AddPrinter(saved_printer, PrinterClass::kSaved);
+
+  chromeos::CupsPrinterStatus printer_status("printer_id");
+  printers_manager().SetPrinterStatus(printer_status);
+
+  crosapi::mojom::GetOAuthAccessTokenResultPtr result;
+  base::RunLoop loop;
+  local_printer_ash()->GetOAuthAccessToken(
+      "printer_id", base::BindOnce(&RecordOAuthAccessToken, std::ref(result),
+                                   loop.QuitClosure()));
+  loop.Run();
+
+  ASSERT_TRUE(result);
+  EXPECT_TRUE(result->is_none());
+}
+
+TEST_F(LocalPrinterAshWithOAuth2Test, GetOAuthAccessTokenOAuthConnectionError) {
+  Printer saved_printer =
+      CreateTestPrinter("printer_id", "saved", "description1");
+  printers_manager().AddPrinter(saved_printer, PrinterClass::kSaved);
+
+  chromeos::CupsPrinterStatus printer_status("printer_id");
+  printer_status.SetAuthenticationInfo({"https://server/url", "scope"});
+  printers_manager().SetPrinterStatus(printer_status);
+
+  EXPECT_CALL(auth_manager(), GetEndpointAccessToken(testing::_, testing::_,
+                                                     "scope", testing::_))
+      .WillOnce([](const GURL& auth_server, const chromeos::Uri& ipp_endpoint,
+                   const std::string& scope,
+                   ash::printing::oauth2::StatusCallback callback) {
+        EXPECT_EQ(auth_server.spec(), "https://server/url");
+        std::move(callback).Run(
+            ash::printing::oauth2::StatusCode::kConnectionError,
+            "error_message");
+      });
+
+  crosapi::mojom::GetOAuthAccessTokenResultPtr result;
+  base::RunLoop loop;
+  local_printer_ash()->GetOAuthAccessToken(
+      "printer_id", base::BindOnce(&RecordOAuthAccessToken, std::ref(result),
+                                   loop.QuitClosure()));
+  loop.Run();
+
+  ASSERT_TRUE(result);
+  EXPECT_TRUE(result->is_error());
+}
+
+TEST_F(LocalPrinterAshWithOAuth2Test, GetOAuthAccessTokenSuccess) {
+  Printer saved_printer =
+      CreateTestPrinter("printer_id", "saved", "description1");
+  printers_manager().AddPrinter(saved_printer, PrinterClass::kSaved);
+
+  chromeos::CupsPrinterStatus printer_status("printer_id");
+  printer_status.SetAuthenticationInfo({"https://server/url", "scope"});
+  printers_manager().SetPrinterStatus(printer_status);
+
+  EXPECT_CALL(auth_manager(), GetEndpointAccessToken(testing::_, testing::_,
+                                                     "scope", testing::_))
+      .WillOnce([](const GURL& auth_server, const chromeos::Uri& ipp_endpoint,
+                   const std::string& scope,
+                   ash::printing::oauth2::StatusCallback callback) {
+        EXPECT_EQ(auth_server.spec(), "https://server/url");
+        std::move(callback).Run(ash::printing::oauth2::StatusCode::kOK,
+                                "access_token");
+      });
+
+  crosapi::mojom::GetOAuthAccessTokenResultPtr result;
+  base::RunLoop loop;
+  local_printer_ash()->GetOAuthAccessToken(
+      "printer_id", base::BindOnce(&RecordOAuthAccessToken, std::ref(result),
+                                   loop.QuitClosure()));
+  loop.Run();
+
+  ASSERT_TRUE(result);
+  ASSERT_TRUE(result->is_token());
+  EXPECT_EQ(result->get_token()->token, "access_token");
+}
+
 }  // namespace printing
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 8f7627d..c034411f 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -859,6 +859,10 @@
   if (!command_line.HasSwitch(switches::kRestoreLastSession))
     restart_command.AppendSwitch(switches::kRestoreLastSession);
 
+  // This is used when recording launch mode metric.
+  if (!command_line.HasSwitch(switches::kRestart))
+    restart_command.AppendSwitch(switches::kRestart);
+
   // TODO(crbug.com/964541): Remove other unneeded switches, including
   // duplicates, perhaps harmonize with switches::RemoveSwitchesForAutostart.
   return restart_command;
diff --git a/chrome/browser/chrome_browser_main_win_unittest.cc b/chrome/browser/chrome_browser_main_win_unittest.cc
index 42f8a242..1814a32 100644
--- a/chrome/browser/chrome_browser_main_win_unittest.cc
+++ b/chrome/browser/chrome_browser_main_win_unittest.cc
@@ -27,7 +27,7 @@
 
   // Simple command line with just the program.
   const base::CommandLine::StringType kNoArgsResult =
-      L" --restore-last-session";
+      L" --restore-last-session --restart";
   base::CommandLine restart_command_line =
       ChromeBrowserMainPartsWin::GetRestartCommandLine(simple_command_line);
   EXPECT_EQ(restart_command_line.GetCommandLineString(), kNoArgsResult);
@@ -42,7 +42,7 @@
   // Command line with a retained switch.
   const std::string kRetainedSwitch = "--enable-sandbox-audio";
   const base::CommandLine::StringType kRetainedSwitchResult =
-      L" --enable-sandbox-audio --restore-last-session";
+      L" --enable-sandbox-audio --restore-last-session --restart";
   base::CommandLine retained_switch_command_line(chrome_path);
   retained_switch_command_line.AppendSwitch(kRetainedSwitch);
   restart_command_line = ChromeBrowserMainPartsWin::GetRestartCommandLine(
@@ -85,7 +85,7 @@
   EXPECT_EQ(restart_command_line.GetCommandLineString(),
             L" --enable-features=Exp2 --enable-foo"
             L" --enable-sandbox-audio"
-            L" --restore-last-session");
+            L" --restore-last-session --restart");
 }
 
 // Test RegisterApplicationRestart to make sure there are no crashes.
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c362a60..ceff44e1 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1049,6 +1049,8 @@
     "../ash/policy/handlers/fake_device_name_policy_handler.h",
     "../ash/policy/handlers/minimum_version_policy_test_helpers.cc",
     "../ash/policy/handlers/minimum_version_policy_test_helpers.h",
+    "../ash/printing/oauth2/mock_authorization_zones_manager.cc",
+    "../ash/printing/oauth2/mock_authorization_zones_manager.h",
     "../ash/printing/printing_stubs.cc",
     "../ash/printing/printing_stubs.h",
     "../ash/printing/test_cups_print_job_manager.cc",
@@ -1820,8 +1822,6 @@
     "../ash/printing/oauth2/http_exchange_unittest.cc",
     "../ash/printing/oauth2/ipp_endpoint_token_fetcher_unittest.cc",
     "../ash/printing/oauth2/log_entry_unittest.cc",
-    "../ash/printing/oauth2/mock_authorization_zones_manager.cc",
-    "../ash/printing/oauth2/mock_authorization_zones_manager.h",
     "../ash/printing/oauth2/profile_auth_servers_sync_bridge_unittest.cc",
     "../ash/printing/oauth2/test_authorization_server.cc",
     "../ash/printing/oauth2/test_authorization_server.h",
diff --git a/chrome/browser/enterprise/idle/browser_closer.cc b/chrome/browser/enterprise/idle/browser_closer.cc
new file mode 100644
index 0000000..2eb62a03
--- /dev/null
+++ b/chrome/browser/enterprise/idle/browser_closer.cc
@@ -0,0 +1,165 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/idle/browser_closer.h"
+
+#include <iterator>
+#include <utility>
+
+#include "base/check.h"
+#include "base/check_is_test.h"
+#include "base/containers/contains.h"
+#include "base/ranges/algorithm.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+
+namespace enterprise_idle {
+
+namespace {
+
+constexpr base::TimeDelta kDialogTimeout = base::Seconds(30);
+
+bool ProfileHasBrowsers(const Profile* profile) {
+  DCHECK(profile);
+  profile = profile->GetOriginalProfile();
+  return base::ranges::any_of(
+      *BrowserList::GetInstance(), [profile](Browser* browser) {
+        return browser->profile()->GetOriginalProfile() == profile;
+      });
+}
+
+}  // namespace
+
+// static
+BrowserCloser* BrowserCloser::GetInstance() {
+  static base::NoDestructor<BrowserCloser> instance;
+  return instance.get();
+}
+
+BrowserCloser::BrowserCloser() = default;
+
+BrowserCloser::~BrowserCloser() = default;
+
+base::CallbackListSubscription BrowserCloser::ShowDialogAndCloseBrowsers(
+    Profile* profile,
+    base::TimeDelta threshold,
+    base::OnceCallback<void(CloseResult)> on_finished) {
+  if (!ProfileHasBrowsers(profile)) {
+    // No browsers to close for this profile. No need to show a dialog or close
+    // browsers, so finish immediately.
+    std::move(on_finished).Run(CloseResult::kSkip);
+    return base::CallbackListSubscription();
+  }
+
+  // Passed the guards: we're really going to show the dialog and close
+  // browsers.
+  closing_profiles_.insert(profile->GetPath());
+  base::CallbackListSubscription subscription =
+      callbacks_.Add(std::move(on_finished));
+
+  if (dialog_) {
+    // The dialog is already visible, re-use it.
+    return subscription;
+  }
+
+  dialog_ = IdleDialog::Show(
+      kDialogTimeout, threshold,
+      base::BindRepeating(&BrowserCloser::OnDialogDismissedByUser,
+                          base::Unretained(this)));
+  dialog_timer_.Start(
+      FROM_HERE, kDialogTimeout,
+      base::BindOnce(&BrowserCloser::OnDialogExpired, base::Unretained(this)));
+  return subscription;
+}
+
+void BrowserCloser::DismissDialogForTesting() {
+  CHECK_IS_TEST();
+  OnDialogDismissedByUser();
+}
+
+void BrowserCloser::OnDialogExpired() {
+  DCHECK(!closing_profiles_.empty());
+
+  if (dialog_)
+    dialog_->Close();
+  dialog_.reset();
+  dialog_timer_.Stop();
+
+  // If we did CloseAllbrowsersWithProfile() right away, OnCloseSuccess() might
+  // run immediately, in which case we would try to modify `closing_profiles_`
+  // while iterating on it.
+  //
+  // Collect the profiles in `profiles_to_close`, and iterate on *that* instead.
+  base::flat_set<Profile*> profiles_to_close;
+  base::ranges::transform(
+      closing_profiles_,
+      std::inserter(profiles_to_close, profiles_to_close.end()),
+      [](const base::FilePath& profile_dir) {
+        return g_browser_process->profile_manager()->GetProfileByPath(
+            profile_dir);
+      });
+
+  for (Profile* profile : profiles_to_close) {
+    if (!ProfileHasBrowsers(profile)) {
+      // Can't close a profile with no browsers. The browsers may have been
+      // closed programmatically (e.g. by an extension) during the 30s delay.
+      closing_profiles_.erase(profile->GetPath());
+      continue;
+    }
+    // TODO(crbug.com/1316551): Get customer feedback on whether
+    // skip_beforeunload should be true or false.
+    BrowserList::CloseAllBrowsersWithProfile(
+        profile,
+        base::BindRepeating(&BrowserCloser::OnCloseSuccess,
+                            base::Unretained(this)),
+        base::BindRepeating(&BrowserCloser::OnCloseAborted,
+                            base::Unretained(this)),
+        /*skip_beforeunload=*/true);
+  }
+
+  // We showed the dialog, but then no profiles needed closing. Count this as a
+  // "success".
+  if (closing_profiles_.empty())
+    OnCloseSuccess(base::FilePath());
+}
+
+void BrowserCloser::OnDialogDismissedByUser() {
+  if (closing_profiles_.empty())
+    return;
+
+  if (dialog_)
+    dialog_->Close();
+  dialog_.reset();
+  dialog_timer_.Stop();
+
+  callbacks_.Notify(CloseResult::kAborted);
+  closing_profiles_.clear();
+}
+
+void BrowserCloser::OnCloseSuccess(const base::FilePath& profile_dir) {
+  if (!profile_dir.empty() && !base::Contains(closing_profiles_, profile_dir))
+    return;  // Out of date.
+
+  // TODO(crbug.com/1316551): Reset `closing_profiles_` if something weird
+  // happens (e.g. new browser window is created by an extension).
+  closing_profiles_.erase(profile_dir);
+  if (!closing_profiles_.empty())
+    return;  // There are profiles left to close still.
+
+  callbacks_.Notify(CloseResult::kSuccess);
+  closing_profiles_.clear();
+}
+
+void BrowserCloser::OnCloseAborted(const base::FilePath& profile_dir) {
+  if (!base::Contains(closing_profiles_, profile_dir))
+    return;  // Out of date.
+
+  callbacks_.Notify(CloseResult::kAborted);
+  closing_profiles_.clear();
+}
+
+}  // namespace enterprise_idle
diff --git a/chrome/browser/enterprise/idle/browser_closer.h b/chrome/browser/enterprise/idle/browser_closer.h
new file mode 100644
index 0000000..a6c7594
--- /dev/null
+++ b/chrome/browser/enterprise/idle/browser_closer.h
@@ -0,0 +1,90 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_IDLE_BROWSER_CLOSER_H_
+#define CHROME_BROWSER_ENTERPRISE_IDLE_BROWSER_CLOSER_H_
+
+#include "base/callback_list.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file_path.h"
+#include "base/no_destructor.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/ui/idle_dialog.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class Profile;
+
+namespace enterprise_idle {
+
+// The "close_browsers" action is different from other actions.
+//
+// - It shows a 30s dialog before closing, which allows the user to abort the
+//   close.
+//
+// - It runs *before* other actions, so ActionsRunner needs to wait for this
+//   flow to finish (or abort).
+//
+// - If multiple Profiles ask to close at the same time, we want to run other
+//   actions after they're *all* done closing (which is asynchronous).
+//
+// A centralized BrowserCloser singleton receives close requests, and calls the
+// observers when it's done.
+class BrowserCloser {
+ public:
+  enum class CloseResult {
+    // The dialog expired, and then the browsers closed successfully.
+    kSuccess,
+    // One of these 2 scenarios:
+    // - The dialog was dismissed by the user, so we didn't close the browsers.
+    // - We tried to close browsers, but failed for some reason.
+    kAborted,
+    // No browsers to close, so nothing to do. Dialog was not shown.
+    kSkip,
+  };
+
+  static BrowserCloser* GetInstance();
+
+  // Shows the 30s dialog, then closes all browsers with `profile` or one of its
+  // OTR profiles (e.g. Incognito).
+  base::CallbackListSubscription ShowDialogAndCloseBrowsers(
+      Profile* profile,
+      base::TimeDelta threshold,
+      base::OnceCallback<void(CloseResult)> on_finished);
+
+  void DismissDialogForTesting();
+
+ private:
+  friend class base::NoDestructor<BrowserCloser>;
+
+  BrowserCloser();
+  ~BrowserCloser();
+
+  // Runs after 30s without the user dismissing the dialog.
+  void OnDialogExpired();
+
+  // Runs when the user hits Escape, or clicks the "Continue using Chrome"
+  // button in the dialog.
+  void OnDialogDismissedByUser();
+
+  // Callbacks for BrowserList::CloseAllBrowsersWithProfile().
+  void OnCloseSuccess(const base::FilePath& profile_dir);
+  void OnCloseAborted(const base::FilePath& profile_dir);
+
+  // Set of profiles that are currently closing. Stored as FilePaths instead of
+  // Profile*, so we don't have to worry about dangling profile pointers.
+  base::flat_set<base::FilePath> closing_profiles_;
+
+  // Pending `on_finished` callbacks.
+  base::OnceCallbackList<void(CloseResult)> callbacks_;
+
+  base::WeakPtr<views::Widget> dialog_;
+
+  // Timer for `dialog_`. Runs OnDialogExpired().
+  base::OneShotTimer dialog_timer_;
+};
+
+}  // namespace enterprise_idle
+
+#endif  // CHROME_BROWSER_ENTERPRISE_IDLE_BROWSER_CLOSER_H_
diff --git a/chrome/browser/enterprise/idle/browser_closer_unittest.cc b/chrome/browser/enterprise/idle/browser_closer_unittest.cc
new file mode 100644
index 0000000..c2ed121
--- /dev/null
+++ b/chrome/browser/enterprise/idle/browser_closer_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/idle/browser_closer.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/time/time.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/views/test/scoped_views_test_helper.h"
+
+namespace enterprise_idle {
+
+using CloseResult = BrowserCloser::CloseResult;
+using base::test::RunClosure;
+
+class BrowserCloserTest : public BrowserWithTestWindowTest {
+ public:
+  BrowserCloserTest()
+      : BrowserWithTestWindowTest(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
+  void SetUp() override {
+    test_views_delegate()->set_use_desktop_native_widgets(true);
+    BrowserWithTestWindowTest::SetUp();
+  }
+};
+
+TEST_F(BrowserCloserTest, Basic) {
+  base::RunLoop run_loop;
+  base::MockOnceCallback<void(CloseResult)> callback;
+  EXPECT_CALL(callback, Run(CloseResult::kSuccess))
+      .WillOnce(RunClosure(run_loop.QuitClosure()));
+  auto subscription = BrowserCloser::GetInstance()->ShowDialogAndCloseBrowsers(
+      profile(), base::Minutes(5), callback.Get());
+  task_environment()->FastForwardBy(base::Seconds(30));
+  run_loop.Run();
+}
+
+TEST_F(BrowserCloserTest, DismissedByUser) {
+  base::RunLoop run_loop;
+  base::MockOnceCallback<void(CloseResult)> callback;
+  EXPECT_CALL(callback, Run(CloseResult::kAborted))
+      .WillOnce(RunClosure(run_loop.QuitClosure()));
+  auto subscription = BrowserCloser::GetInstance()->ShowDialogAndCloseBrowsers(
+      profile(), base::Minutes(5), callback.Get());
+  BrowserCloser::GetInstance()->DismissDialogForTesting();
+  run_loop.Run();
+}
+
+TEST_F(BrowserCloserTest, ProfileHasNoBrowsers) {
+  set_browser(nullptr);
+
+  base::RunLoop run_loop;
+  base::MockOnceCallback<void(CloseResult)> callback;
+  EXPECT_CALL(callback, Run(CloseResult::kSkip))
+      .WillOnce(RunClosure(run_loop.QuitClosure()));
+  auto subscription = BrowserCloser::GetInstance()->ShowDialogAndCloseBrowsers(
+      profile(), base::Minutes(5), callback.Get());
+  run_loop.Run();
+}
+
+}  // namespace enterprise_idle
diff --git a/chrome/browser/enterprise/idle/idle_service.cc b/chrome/browser/enterprise/idle/idle_service.cc
index bfd169ae..77f43425 100644
--- a/chrome/browser/enterprise/idle/idle_service.cc
+++ b/chrome/browser/enterprise/idle/idle_service.cc
@@ -4,231 +4,17 @@
 
 #include "chrome/browser/enterprise/idle/idle_service.h"
 
+#include <algorithm>
+
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/containers/flat_set.h"
-#include "base/files/file_path.h"
-#include "base/memory/singleton.h"
-#include "base/no_destructor.h"
-#include "base/ranges/algorithm.h"
-#include "chrome/browser/enterprise/idle/idle_service_factory.h"
+#include "chrome/browser/enterprise/idle/browser_closer.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/idle_dialog.h"
 #include "chrome/browser/ui/profile_picker.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/idle/idle.h"
 #include "ui/base/idle/idle_polling_service.h"
-#include "ui/views/widget/widget.h"
-
-namespace {
-
-bool ProfileHasBrowsers(const Profile* profile) {
-  DCHECK(profile);
-  profile = profile->GetOriginalProfile();
-  return base::ranges::any_of(
-      *BrowserList::GetInstance(), [profile](Browser* browser) {
-        return browser->profile()->GetOriginalProfile() == profile;
-      });
-}
-
-// Keeps track of the idle state of each Profile. Keeping all the states in a
-// single place means we can do batch actions without duplicating. For instance,
-// if 2 Profiles have the same threshold we can close all their windows, wait
-// for BOTH profiles to close, and only show the Profile Picker once.
-class IdleRegistry : public ui::IdlePollingService::Observer {
- public:
-  static IdleRegistry& GetInstance() {
-    static base::NoDestructor<IdleRegistry> instance;
-    return *instance;
-  }
-
-  // Start tracking a new Profile, or update its threshold if it's already being
-  // tracked. Called when the IdleProfileCloseTimeout policy changes value.
-  void AddOrUpdate(Profile* profile, base::TimeDelta threshold) {
-    DCHECK(!profile->IsSystemProfile());
-    DCHECK(!profile->IsOffTheRecord());
-    if (profiles_.find(profile) == profiles_.end()) {
-      profiles_[profile].threshold = threshold;
-    }
-    if (!polling_service_observation_.IsObserving()) {
-      polling_service_observation_.Observe(
-          ui::IdlePollingService::GetInstance());
-    }
-  }
-
-  // Stop tracking a Profile, if it's being tracked. Called during shutdown, or
-  // when the IdleProfileCloseTimeout policy becomes unset.
-  //
-  // If the profile is not tracked, this is a no-op.
-  void Remove(Profile* profile) {
-    profiles_.erase(profile);
-    if (profiles_.empty())
-      polling_service_observation_.Reset();
-  }
-
-  void SetDialogTimeoutForTesting(base::TimeDelta dialog_timeout) {
-    dialog_timeout_ = dialog_timeout;
-  }
-
- private:
-  friend struct base::DefaultSingletonTraits<IdleRegistry>;
-
-  // See `profiles_`.
-  struct ProfileState {
-    base::TimeDelta threshold;  // From the IdleProfileCloseTimeout policy.
-  };
-
-  // ui::IdlePollingService::Observer:
-  void OnIdleStateChange(
-      const ui::IdlePollingService::State& polled_state) override {
-    base::flat_set<Profile*> profiles_to_close;
-
-    for (auto& [profile, state] : profiles_) {
-      if (polled_state.idle_time < state.threshold)
-        continue;  // Profile is not idle.
-      if (base::Contains(closing_profiles_, profile->GetPath()))
-        continue;  // Profile is already closing.
-      if (!ProfileHasBrowsers(profile))
-        continue;  // Can't close a profile with no browsers...
-
-      // Profile just became idle.
-      profiles_to_close.insert(profile);
-    }
-
-    if (!profiles_to_close.empty()) {
-      // One or more profiles just became idle. Show the dialog, and start the
-      // 30s timer.
-
-      // TODO(nicolaso): Don't show it every time, i.e. if it's already visible
-      // from a previous profile becoming idle right before this one. Running
-      // this code multiple times like that could cause race conditions/weird
-      // behaviour.
-      //
-      // ... but as currently written, it's impossible to reach that state. The
-      // dialog only shows for 30s, and there's at least 60s between 2 different
-      // profiles triggering idle state non-simultaneously.
-      //
-      // For now, this CHECK() should be enough.
-      DCHECK(closing_profiles_.empty());
-
-      should_open_profile_picker_ = true;
-      for (Profile* profile : profiles_to_close)
-        closing_profiles_.insert(profile->GetPath());
-
-      dialog_ = IdleDialog::Show(
-          dialog_timeout_, profiles_[*profiles_to_close.begin()].threshold,
-          base::BindRepeating(&IdleRegistry::OnDialogClosedByUser,
-                              base::Unretained(this)));
-      dialog_timer_.Start(FROM_HERE, dialog_timeout_,
-                          base::BindOnce(&IdleRegistry::OnDialogExpired,
-                                         base::Unretained(this)));
-    }
-  }
-
-  // Abort the close operation.
-  void OnDialogClosedByUser() {
-    closing_profiles_.clear();
-    should_open_profile_picker_ = false;
-    if (dialog_)
-      dialog_->Close();
-    dialog_.reset();
-    dialog_timer_.Stop();
-  }
-
-  // Perform the close operation, then show the profile picker in the callback
-  // to CloseAllBrowsersWithProfile().
-  void OnDialogExpired() {
-    DCHECK(!closing_profiles_.empty());
-    DCHECK(should_open_profile_picker_);
-
-    if (dialog_)
-      dialog_->Close();
-    dialog_.reset();
-
-    for (auto& [profile, state] : profiles_) {
-      if (!base::Contains(closing_profiles_, profile->GetPath()))
-        continue;
-      if (!ProfileHasBrowsers(profile)) {
-        // Can't close a profile with no browsers. The browsers may have been
-        // closed programmatically (e.g. by an extension) during the 30s delay.
-        closing_profiles_.erase(profile->GetPath());
-        continue;
-      }
-      // TODO(crbug.com/1316551): Get customer feedback on whether
-      // skip_beforeunload should be true or false.
-      BrowserList::CloseAllBrowsersWithProfile(
-          profile,
-          base::BindRepeating(&IdleRegistry::OnCloseSuccess,
-                              base::Unretained(this)),
-          base::BindRepeating(&IdleRegistry::OnCloseAborted,
-                              base::Unretained(this)),
-          /*skip_beforeunload=*/true);
-    }
-
-    if (closing_profiles_.empty()) {
-      // If no profile had any browsers, then we're not actually closing.
-      should_open_profile_picker_ = false;
-    }
-  }
-
-  void OnCloseAborted(const base::FilePath& profile_dir) {
-    // TODO(crbug.com/1316551): What should we do if the profile's been "closed"
-    // and *then* a new window is created?
-    closing_profiles_.erase(profile_dir);
-    // Something aborted the close. Don't show the profile picker.
-    should_open_profile_picker_ = false;
-  }
-
-  // BrowserListObserver:
-  void OnCloseSuccess(const base::FilePath& profile_dir) {
-    // TODO(crbug.com/1316551): Reset `closing_profiles_` and
-    // `should_open_profile_picker_` if something weird happens (e.g. new
-    // browser window is created by an extension).
-    if (profiles_.empty())
-      return;
-    if (!base::Contains(closing_profiles_, profile_dir))
-      return;
-
-    closing_profiles_.erase(profile_dir);
-
-    if (closing_profiles_.empty() && should_open_profile_picker_) {
-      // All windows are done closing for idle profiles. Show the Profile
-      // Picker.
-      should_open_profile_picker_ = false;
-      ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
-          ProfilePicker::EntryPoint::kProfileIdle));
-    }
-  }
-
-  // Set of profiles being closed right now. Filled in when idle logic triggers
-  // and becomes empty again when:
-  //
-  // (a) All idle browsers finish closing.
-  // (b) The user aborts closing by dismissing the dialog.
-  base::flat_set<base::FilePath> closing_profiles_;
-  // Whether to open the profile picker after the last profile in
-  // `closing_profiles_` finishes closing.
-  bool should_open_profile_picker_ = false;
-
-  std::map<Profile*, ProfileState> profiles_;
-
-  // Dialog shown for 30s before doing the close operation. If the user
-  // dismisses it or clicks "Continue using Chrome", the idle timer resets to
-  // 0s.
-  base::WeakPtr<views::Widget> dialog_;
-  // 30s timer for |dialog_|.
-  base::OneShotTimer dialog_timer_;
-  base::TimeDelta dialog_timeout_ = base::Seconds(30);
-
-  base::ScopedObservation<ui::IdlePollingService,
-                          ui::IdlePollingService::Observer>
-      polling_service_observation_{this};
-};
-
-}  // namespace
 
 namespace enterprise_idle {
 
@@ -242,27 +28,67 @@
   OnIdleProfileCloseTimeoutPrefChanged();
 }
 
-IdleService::~IdleService() {
-  IdleRegistry::GetInstance().Remove(profile_);
-}
-
-void IdleService::Shutdown() {
-  IdleRegistry::GetInstance().Remove(profile_);
-}
-
-void IdleService::SetDialogTimeoutForTesting(base::TimeDelta dialog_timeout) {
-  IdleRegistry::GetInstance().SetDialogTimeoutForTesting(  // IN-TEST
-      dialog_timeout);
-}
+IdleService::~IdleService() = default;
 
 void IdleService::OnIdleProfileCloseTimeoutPrefChanged() {
   int minutes =
       profile_->GetPrefs()->GetInteger(prefs::kIdleProfileCloseTimeout);
   if (minutes > 0) {
-    IdleRegistry::GetInstance().AddOrUpdate(
-        profile_, std::max(base::Minutes(5), base::Minutes(minutes)));
+    // TODO(crbug.com/1316551): Validate the policy value (e.g. clamp to a
+    // minimum) in a PolicyHandler, instead of here.
+    minutes = std::max(5, minutes);
+    // `is_idle_` will auto-update in 1 second, no need to set it here.
+    idle_threshold_ = base::Minutes(minutes);
+    if (!polling_service_observation_.IsObserving()) {
+      polling_service_observation_.Observe(
+          ui::IdlePollingService::GetInstance());
+    }
   } else {
-    IdleRegistry::GetInstance().Remove(profile_);
+    is_idle_ = false;
+    idle_threshold_ = base::TimeDelta();
+    polling_service_observation_.Reset();
+  }
+}
+
+void IdleService::OnIdleStateChange(
+    const ui::IdlePollingService::State& polled_state) {
+  if (is_idle_) {
+    if (polled_state.idle_time < idle_threshold_) {
+      // Profile just stopped being idle.
+      is_idle_ = false;
+    }
+  } else {
+    if (polled_state.idle_time >= idle_threshold_) {
+      // Profile just became idle. Show the dialog.
+      is_idle_ = true;
+      browser_close_subscription_ =
+          BrowserCloser::GetInstance()->ShowDialogAndCloseBrowsers(
+              profile_, idle_threshold_,
+              base::BindOnce(&IdleService::OnCloseFinished,
+                             weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
+}
+
+void IdleService::OnCloseFinished(BrowserCloser::CloseResult result) {
+  switch (result) {
+    case BrowserCloser::CloseResult::kSuccess:
+      // Technically, this shows the ProfilePicker once per profile. However,
+      // all IdleServices run OnCloseFinished() in succession (once they're
+      // *all* closed), and there's only one ProfilePicker.
+      //
+      // Calling ProfilePicker::Show() multiple times like
+      // this is a no-op, so we don't need to bother de-duping work.
+      ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
+          ProfilePicker::EntryPoint::kProfileIdle));
+      break;
+
+    case BrowserCloser::CloseResult::kAborted:
+    case BrowserCloser::CloseResult::kSkip:
+      break;
+
+    default:
+      NOTREACHED();
   }
 }
 
diff --git a/chrome/browser/enterprise/idle/idle_service.h b/chrome/browser/enterprise/idle/idle_service.h
index 486968c..66492eb8 100644
--- a/chrome/browser/enterprise/idle/idle_service.h
+++ b/chrome/browser/enterprise/idle/idle_service.h
@@ -5,9 +5,14 @@
 #ifndef CHROME_BROWSER_ENTERPRISE_IDLE_IDLE_SERVICE_H_
 #define CHROME_BROWSER_ENTERPRISE_IDLE_IDLE_SERVICE_H_
 
+#include "base/callback_list.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
+#include "chrome/browser/enterprise/idle/browser_closer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
+#include "ui/base/idle/idle_polling_service.h"
 
 class Profile;
 
@@ -17,7 +22,8 @@
 // policy. Keeps track of the policy's value, and listens for idle events.
 // Closes the profile's window when it becomes idle, and shows the profile
 // picker.
-class IdleService : public KeyedService {
+class IdleService : public KeyedService,
+                    public ui::IdlePollingService::Observer {
  public:
   explicit IdleService(Profile* profile);
 
@@ -26,19 +32,32 @@
 
   ~IdleService() override;
 
-  // KeyedService:
-  void Shutdown() override;
-
-  static void SetDialogTimeoutForTesting(base::TimeDelta dialog_timeout);
+  // ui::IdlePollingService::Observer:
+  void OnIdleStateChange(
+      const ui::IdlePollingService::State& polled_state) override;
 
  private:
   // Called when the IdleProfileCloseTimeout policy changes, via the
   // "idle_profile_close_timeout" pref it's mapped to.
   void OnIdleProfileCloseTimeoutPrefChanged();
 
+  // Runs when the BrowserCloser finishes. Depending on the result, shows the
+  // Profile Picker.
+  void OnCloseFinished(BrowserCloser::CloseResult result);
+
+  raw_ptr<Profile> const profile_;
   PrefChangeRegistrar pref_change_registrar_;
 
-  Profile* profile_;
+  bool is_idle_ = false;
+  base::TimeDelta idle_threshold_;
+
+  base::CallbackListSubscription browser_close_subscription_;
+
+  base::ScopedObservation<ui::IdlePollingService,
+                          ui::IdlePollingService::Observer>
+      polling_service_observation_{this};
+
+  base::WeakPtrFactory<IdleService> weak_ptr_factory_{this};
 };
 
 }  // namespace enterprise_idle
diff --git a/chrome/browser/enterprise/idle/idle_service_browsertest.cc b/chrome/browser/enterprise/idle/idle_service_browsertest.cc
index d92b0a2..283742b 100644
--- a/chrome/browser/enterprise/idle/idle_service_browsertest.cc
+++ b/chrome/browser/enterprise/idle/idle_service_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/timer/mock_timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/idle/browser_closer.h"
 #include "chrome/browser/enterprise/idle/idle_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
@@ -93,7 +94,6 @@
     scoped_idle_provider_ =
         std::make_unique<ui::test::ScopedIdleProviderForTest>(
             std::move(time_provider));
-    IdleService::SetDialogTimeoutForTesting(base::Milliseconds(0));
   }
 
   void TearDownOnMainThread() override {
@@ -145,16 +145,47 @@
   task_runner()->FastForwardBy(base::Seconds(1));
   EXPECT_EQ(1, GetBrowserCount(profile));
 
-  // 300s, threshold is reached. Close browsers, then show the Profile Picker.
+  // 300s, threshold is reached. This should show the dialog.
   EXPECT_CALL(provider(), CalculateIdleTime())
       .WillOnce(Return(base::Seconds(300)));
-  BrowserCloseWaiter waiter({browser()});
   task_runner()->FastForwardBy(base::Seconds(1));
+  EXPECT_EQ(1, GetBrowserCount(profile));
+
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillRepeatedly(Return(base::Seconds(15)));
+  BrowserCloseWaiter waiter({browser()});
+  task_runner()->FastForwardBy(base::Seconds(30));
   waiter.Wait();
   EXPECT_EQ(0, GetBrowserCount(profile));
   EXPECT_TRUE(ProfilePicker::IsOpen());
 }
 
+IN_PROC_BROWSER_TEST_F(IdleServiceTest, DidNotClose) {
+  ON_CALL(provider(), CheckIdleStateIsLocked()).WillByDefault(Return(false));
+
+  // Set the IdleProfileCloseTimeout policy to 1 minute, which should round up
+  // to 5 minutes (the minimum).
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillOnce(Return(base::Seconds(298)));
+  Profile* profile = browser()->profile();
+  profile->GetPrefs()->SetInteger("idle_profile_close_timeout", 1);
+
+  EXPECT_EQ(1, GetBrowserCount(profile));
+
+  // 300s, threshold is reached. The user dismisses the dialog though, so we do
+  // nothing.
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillOnce(Return(base::Seconds(300)));
+  task_runner()->FastForwardBy(base::Seconds(1));
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillRepeatedly(Return(base::Seconds(301)));
+  BrowserCloser::GetInstance()->DismissDialogForTesting();
+  task_runner()->FastForwardBy(base::Seconds(30));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, GetBrowserCount(profile));
+  EXPECT_FALSE(ProfilePicker::IsOpen());
+}
+
 IN_PROC_BROWSER_TEST_F(IdleServiceTest, TenMinutes) {
   ON_CALL(provider(), CheckIdleStateIsLocked()).WillByDefault(Return(false));
 
@@ -175,8 +206,13 @@
   // 600s, threshold is reached. Close browsers, then show the Profile Picker.
   EXPECT_CALL(provider(), CalculateIdleTime())
       .WillOnce(Return(base::Seconds(600)));
-  BrowserCloseWaiter waiter({browser()});
   task_runner()->FastForwardBy(base::Seconds(1));
+  EXPECT_EQ(1, GetBrowserCount(profile));
+
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillRepeatedly(Return(base::Seconds(301)));
+  BrowserCloseWaiter waiter({browser()});
+  task_runner()->FastForwardBy(base::Seconds(30));
   waiter.Wait();
   EXPECT_EQ(0, GetBrowserCount(profile));
   EXPECT_TRUE(ProfilePicker::IsOpen());
@@ -236,8 +272,12 @@
   // 300s, threshold is reached. Close browsers, then show the Profile Picker.
   EXPECT_CALL(provider(), CalculateIdleTime())
       .WillOnce(Return(base::Seconds(300)));
-  BrowserCloseWaiter waiter({browser(), browser2, browser3});
   task_runner()->FastForwardBy(base::Seconds(1));
+
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillRepeatedly(Return(base::Seconds(315)));
+  BrowserCloseWaiter waiter({browser(), browser2, browser3});
+  task_runner()->FastForwardBy(base::Seconds(30));
   waiter.Wait();
   EXPECT_EQ(0, GetBrowserCount(profile));
   EXPECT_EQ(0, GetBrowserCount(profile2));
@@ -281,9 +321,12 @@
   // Profile Picker.
   EXPECT_CALL(provider(), CalculateIdleTime())
       .WillOnce(Return(base::Seconds(300)));
+  task_runner()->FastForwardBy(base::Seconds(1));
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillRepeatedly(Return(base::Seconds(315)));
   {
     BrowserCloseWaiter waiter({browser(), browser2});
-    task_runner()->FastForwardBy(base::Seconds(1));
+    task_runner()->FastForwardBy(base::Seconds(30));
     waiter.Wait();
   }
   EXPECT_EQ(0, GetBrowserCount(profile));
@@ -293,9 +336,12 @@
   // 360s, threshold is reached for `profile2`. Close its browsers.
   EXPECT_CALL(provider(), CalculateIdleTime())
       .WillOnce(Return(base::Seconds(360)));
+  task_runner()->FastForwardBy(base::Seconds(1));
+  EXPECT_CALL(provider(), CalculateIdleTime())
+      .WillRepeatedly(Return(base::Seconds(375)));
   {
     BrowserCloseWaiter waiter({browser3});
-    task_runner()->FastForwardBy(base::Seconds(1));
+    task_runner()->FastForwardBy(base::Seconds(30));
     waiter.Wait();
   }
   EXPECT_EQ(0, GetBrowserCount(profile));
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index 5b8aeb7..6a7a19f 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -62,6 +62,10 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #endif
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/shlwapi.h"
+#endif
+
 namespace features {
 BASE_FEATURE(kFileSystemAccessPersistentPermissions,
              "kFileSystemAccessPersistentPermissions",
@@ -270,6 +274,12 @@
   DCHECK(!check_path.empty());
   DCHECK(check_path.IsAbsolute());
 
+#if BUILDFLAG(IS_WIN)
+  // On Windows, UNC paths are rejected to avoid bypassing the block list.
+  if (PathIsUNC(check_path.value().c_str()))
+    return true;
+#endif
+
   base::FilePath nearest_ancestor;
   int nearest_ancestor_path_key = kNoBasePathKey;
   BlockType nearest_ancestor_block_type = kDontBlockChildren;
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index 3887cdd..9f2e3f44 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -395,9 +395,41 @@
                 permission_context(), PathType::kLocal,
                 base::FilePath(FILE_PATH_LITERAL("/dev/foo")),
                 HandleType::kFile, UserAction::kOpen));
+#elif BUILDFLAG(IS_WIN)
+  EXPECT_EQ(SensitiveDirectoryResult::kAbort,
+            ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathType::kLocal,
+                base::FilePath(FILE_PATH_LITERAL("c:\\Program Files")),
+                HandleType::kDirectory, UserAction::kOpen));
 #endif
 }
 
+#if BUILDFLAG(IS_WIN)
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       ConfirmSensitiveEntryAccess_UNCPath) {
+  EXPECT_EQ(
+      SensitiveDirectoryResult::kAbort,
+      ConfirmSensitiveEntryAccessSync(
+          permission_context(), PathType::kLocal,
+          base::FilePath(FILE_PATH_LITERAL("\\\\127.0.0.1\\c:\\Program Files")),
+          HandleType::kDirectory, UserAction::kOpen));
+
+  EXPECT_EQ(
+      SensitiveDirectoryResult::kAbort,
+      ConfirmSensitiveEntryAccessSync(
+          permission_context(), PathType::kLocal,
+          base::FilePath(FILE_PATH_LITERAL("\\\\127.0.0.1\\c:\\foo\\bar")),
+          HandleType::kDirectory, UserAction::kOpen));
+
+  EXPECT_EQ(
+      SensitiveDirectoryResult::kAbort,
+      ConfirmSensitiveEntryAccessSync(
+          permission_context(), PathType::kLocal,
+          base::FilePath(FILE_PATH_LITERAL("\\\\localhost\\c:\\Program Files")),
+          HandleType::kDirectory, UserAction::kOpen));
+}
+#endif
+
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_DangerousFile) {
   // Saving files with a harmless extension should be allowed.
diff --git a/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc b/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc
index a0d83e0..7449498 100644
--- a/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_navigation_throttle.cc
@@ -31,7 +31,7 @@
 FirstPartySetsNavigationThrottle::~FirstPartySetsNavigationThrottle() = default;
 
 ThrottleCheckResult FirstPartySetsNavigationThrottle::WillStartRequest() {
-  if (!service_->is_ready()) {
+  if (service_->is_enabled() && !service_->is_ready()) {
     service_->RegisterThrottleResumeCallback(base::BindOnce(
         &FirstPartySetsNavigationThrottle::Resume, weak_factory_.GetWeakPtr()));
     return content::NavigationThrottle::DEFER;
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
index 173b044..ac7d31e 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
@@ -173,10 +173,8 @@
 void FirstPartySetsPolicyService::RegisterThrottleResumeCallback(
     base::OnceClosure resume_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (is_ready() || !is_enabled()) {
-    std::move(resume_callback).Run();
-    return;
-  }
+  DCHECK(!is_ready());
+  DCHECK(is_enabled());
   on_ready_callbacks_.push_back(std::move(resume_callback));
 }
 
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service.h b/chrome/browser/first_party_sets/first_party_sets_policy_service.h
index a7a601dd..60164a2 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service.h
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service.h
@@ -69,9 +69,8 @@
   // First-Party Sets enabled pref changes.
   void OnFirstPartySetsEnabledChanged(bool enabled);
 
-  // Invoke the callback synchronously to resume navigation if the instance is
-  // ready; or stores the callback to be invoked when this service is ready to
-  // do so.
+  // Stores the callback to be invoked when this service is ready to do so. Must
+  // not be called when FPS is not enabled or the service is already ready.
   void RegisterThrottleResumeCallback(base::OnceClosure resume_callback);
 
   // KeyedService:
@@ -91,6 +90,12 @@
   // Exposes `Init` for use in tests.
   void InitForTesting();
 
+  // Returns true iff the preference and feature are both enabled.
+  bool is_enabled() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return feature_enabled_ && pref_enabled_;
+  }
+
   // Returns true when this instance has received the config thus has been fully
   // initialized.
   bool is_ready() const {
@@ -149,12 +154,6 @@
       const std::set<net::SchemefulSite>& party_context,
       base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const;
 
-  // Returns true iff the preference and feature are both enabled.
-  bool is_enabled() const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return feature_enabled_ && pref_enabled_;
-  }
-
   // The remote delegates associated with the profile that created this
   // service.
   mojo::RemoteSet<network::mojom::FirstPartySetsAccessDelegate>
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
index 0fe3d48..d2cae38 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
@@ -667,46 +667,31 @@
 
 class FirstPartySetsPolicyServiceResumeThrottleTest
     : public FirstPartySetsPolicyServiceTest,
-      public ::testing::WithParamInterface<std::tuple<bool, bool, PrefState>> {
+      public ::testing::WithParamInterface<bool> {
  public:
   FirstPartySetsPolicyServiceResumeThrottleTest() {
-    if (IsFeatureEnabled()) {
-      features_.InitAndEnableFeatureWithParameters(
-          features::kFirstPartySets,
-          {{features::kFirstPartySetsClearSiteDataOnChangedSets.name,
-            IsClearingFeatureEnabled() ? "true" : "false"}});
-    } else {
-      features_.InitAndDisableFeature(features::kFirstPartySets);
-    }
+    features_.InitAndEnableFeatureWithParameters(
+        features::kFirstPartySets,
+        {{features::kFirstPartySetsClearSiteDataOnChangedSets.name,
+          GetParam() ? "true" : "false"}});
   }
 
-  bool IsPrefEnabled() { return GetPrefState() == PrefState::kEnabled; }
-
  private:
-  bool IsFeatureEnabled() { return std::get<0>(GetParam()); }
-  bool IsClearingFeatureEnabled() { return std::get<1>(GetParam()); }
-  PrefState GetPrefState() { return std::get<2>(GetParam()); }
-
   base::test::ScopedFeatureList features_;
 };
 
 // Verify the throttle resume callback is always invoked.
 TEST_P(FirstPartySetsPolicyServiceResumeThrottleTest,
-       MaybeAddNavigationThrottleResumeCallback) {
-  SetEnabledPref(IsPrefEnabled());
+       RegisterThrottleResumeCallback) {
+  SetInvokeCallbacksAsynchronously(true);
+  service()->InitForTesting();
   base::RunLoop run_loop;
   service()->RegisterThrottleResumeCallback(run_loop.QuitClosure());
-  service()->InitForTesting();
   run_loop.Run();
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    FirstPartySetsPolicyServiceResumeThrottleTest,
-    ::testing::Combine(::testing::Bool(),
-                       ::testing::Bool(),
-                       ::testing::Values(PrefState::kDefault,
-                                         PrefState::kDisabled,
-                                         PrefState::kEnabled)));
+INSTANTIATE_TEST_SUITE_P(All,
+                         FirstPartySetsPolicyServiceResumeThrottleTest,
+                         ::testing::Bool());
 
 }  // namespace first_party_sets
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 728ad31..d0b95a4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -661,7 +661,7 @@
   {
     "name": "back-gesture-refactor-android",
     "owners": ["lazzzis@google.com", "jinsukkim", "twellington"],
-    "expiry_milestone": 109
+    "expiry_milestone": 115
   },
   {
     "name": "batch-fetch-requests",
@@ -676,12 +676,12 @@
   {
     "name": "binding-manager-connection-limit",
     "owners": ["ckitagawa", "yfriedman"],
-    "expiry_milestone": 109
+    "expiry_milestone": 111
   },
   {
     "name": "binding-manager-use-not-perceptible-binding",
     "owners": ["ckitagawa", "yfriedman"],
-    "expiry_milestone": 109
+    "expiry_milestone": 111
   },
   {
     "name": "biometric-authentication-for-filling",
@@ -4461,11 +4461,6 @@
     "expiry_milestone": 114
   },
   {
-    "name": "lens-on-quick-action-search-widget",
-    "owners": [ "juanmojica@google.com", "schechter@google.com", "lens-chrome@google.com" ],
-    "expiry_milestone": 107
-  },
-  {
     "name": "lightweight-reactions-android",
     "owners": [ "gujen", "sophey", "chrome-with-friends-robots@google.com" ],
     "expiry_milestone": 110
@@ -5478,7 +5473,7 @@
   {
     "name": "paint-preview-demo",
     "owners": [ "ckitagawa", "fredmello", "chrome-fdt@google.com" ],
-    "expiry_milestone": 109
+    "expiry_milestone": 111
   },
   {
     "name": "paint-preview-startup",
@@ -6026,11 +6021,6 @@
     "expiry_milestone": 115
   },
   {
-    "name": "sameparty-cookies-considered-first-party",
-    "owners": ["cfredric", "chrome-first-party-sets@chromium.org"],
-    "expiry_milestone": 108
-  },
-  {
     "name": "sanitizer-api",
     "owners": [ "//third_party/blink/renderer/modules/sanitizer_api/OWNERS" ],
     "expiry_milestone": 109
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c0629e5..100b7e4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1778,12 +1778,6 @@
     "Enables use of a static page in a new tab when using the Lens region "
     "search feature.";
 
-const char kLensOnQuickActionSearchWidgetName[] =
-    "Google Lens in Chrome's Quick Action Search Widget";
-const char kLensOnQuickActionSearchWidgetDescription[] =
-    "Enable an entry point to Google Lens to allow users to search what they "
-    "see using their mobile camera in the Quick Action Search Widget.";
-
 const char kLogJsConsoleMessagesName[] =
     "Log JS console messages in system logs";
 const char kLogJsConsoleMessagesDescription[] =
@@ -2550,12 +2544,6 @@
     "When enabled, adds the unused sites permission module to Safety Check on "
     "desktop. The module will be shown depending on the browser state.";
 
-const char kSamePartyCookiesConsideredFirstPartyName[] =
-    "Consider SameParty cookies to be first-party.";
-const char kSamePartyCookiesConsideredFirstPartyDescription[] =
-    "If enabled, SameParty cookies will not be blocked even if third-party "
-    "cookies are blocked.";
-
 const char kPartitionedCookiesName[] = "Partitioned cookies";
 const char kPartitionedCookiesDescription[] =
     "Controls if the Partitioned cookie attribute is enabled.";
@@ -6278,10 +6266,6 @@
     "When enabled, the autoSelectAllScreens attribute is available for usage "
     "with the GetDisplayMediaSet API.";
 
-const char kVaapiAV1DecoderName[] = "VA-API decode acceleration for AV1";
-const char kVaapiAV1DecoderDescription[] =
-    "Enable or disable decode acceleration of AV1 videos using the VA-API.";
-
 const char kIntentChipSkipsPickerName[] =
     "Link capturing intent chip skips the intent picker bubble";
 const char kIntentChipSkipsPickerDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 12912ab6..8fa909ec 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1428,9 +1428,6 @@
 extern const char kSafetyCheckUnusedSitePermissionsName[];
 extern const char kSafetyCheckUnusedSitePermissionsDescription[];
 
-extern const char kSamePartyCookiesConsideredFirstPartyName[];
-extern const char kSamePartyCookiesConsideredFirstPartyDescription[];
-
 extern const char kPartitionedCookiesName[];
 extern const char kPartitionedCookiesDescription[];
 // TODO(crbug.com/1296161): Remove this when the CHIPS OT ends.
@@ -3602,9 +3599,6 @@
 extern const char kGetDisplayMediaSetAutoSelectAllScreensName[];
 extern const char kGetDisplayMediaSetAutoSelectAllScreensDescription[];
 
-extern const char kVaapiAV1DecoderName[];
-extern const char kVaapiAV1DecoderDescription[];
-
 extern const char kIntentChipSkipsPickerName[];
 extern const char kIntentChipSkipsPickerDescription[];
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 4114d60..af0b1153 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -4054,7 +4054,9 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(NavigationPageLoadMetricsBrowserTest, FirstInputDelay) {
+// Flaky. See https://crbug.com/1224780.
+IN_PROC_BROWSER_TEST_P(NavigationPageLoadMetricsBrowserTest,
+                       DISABLED_FirstInputDelay) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
@@ -4067,35 +4069,19 @@
 
   // 1) Navigate to url1.
   EXPECT_TRUE(content::NavigateToURL(web_contents(), url1));
-
-  // There is no FirstInputDelay in UMA before the simulated mouse click
-  histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 0);
   content::RenderFrameHost* rfh_a = RenderFrameHost();
   content::RenderProcessHost* rfh_a_process = rfh_a->GetProcess();
 
-  // Create a Performance Observer to ensure the renderer receives the click
-  EXPECT_TRUE(content::ExecJs(web_contents(),
-    "waitFirstInput = async () => {"
-     "const observePromise = new Promise(resolve => {"
-       "new PerformanceObserver(e => {"
-         "e.getEntries().forEach(entry => {"
-           "resolve();"
-         "})"
-       "}).observe({type: 'first-input', buffered: true});"
-     "});"
-     "return await observePromise;"
-     "};"
-  ));
-
   // Simulate mouse click. FirstInputDelay won't get updated immediately.
   content::SimulateMouseClickAt(web_contents(), 0,
                                 blink::WebMouseEvent::Button::kLeft,
                                 gfx::Point(100, 100));
-
-  // Run the Performance Observer
-  EXPECT_TRUE(ExecJs(web_contents(), "waitFirstInput()"));
-
+  // Run arbitrary script and run tasks in the brwoser to ensure the input is
+  // processed in the renderer.
+  EXPECT_TRUE(content::ExecJs(rfh_a, "var foo = 42;"));
+  base::RunLoop().RunUntilIdle();
   content::FetchHistogramsFromChildProcesses();
+  histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 0);
 
   // 2) Immediately navigate to url2.
   if (GetParam() == "CrossSiteRendererInitiated") {
@@ -4107,13 +4093,6 @@
   content::FetchHistogramsFromChildProcesses();
   if (GetParam() != "CrossSiteBrowserInitiated" ||
       rfh_a_process == RenderFrameHost()->GetProcess()) {
-
-    // Let the program wait for 50ms to ensure the First Input Delay
-    // is ready in UMA
-    // TODO(crbug.com/1380108) : replace with test_waiter when it allows multiple page
-    // loads
-    base::PlatformThread::Sleep(base::Milliseconds(50));
-
     // - For "SameSite" case, since the old and new RenderFrame either share a
     // process (with RenderDocument/back-forward cache) or the RenderFrame is
     // reused the metrics update will be sent to the browser during commit and
@@ -4123,7 +4102,6 @@
     // - For "CrossSiteBrowserInitiated" case, if the old and new RenderFrame
     // share a process, the metrics update will be sent to the browser during
     // commit and won't get ignored, successfully updating the histogram.
-
     histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 1);
   } else {
     // Note that in some cases the metrics might flakily get updated in time,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index bd3d290a..67fc241 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -767,6 +767,14 @@
 // Deprecated 09/2022.
 const char kFirstPartySetsEnabled[] = "first_party_sets.enabled";
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Deprecated 10/2022.
+const char kSuggestedContentInfoShownInLauncher[] =
+    "ash.launcher.suggested_content_info_shown";
+const char kSuggestedContentInfoDismissedInLauncher[] =
+    "ash.launcher.suggested_content_info_dismissed";
+#endif
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -1014,6 +1022,13 @@
   // Deprecated 10/2022.
   registry->RegisterBooleanPref(kLoadCryptoTokenExtension, false);
 #endif
+
+// Deprecated 10/2022.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  registry->RegisterIntegerPref(kSuggestedContentInfoShownInLauncher, 0);
+  registry->RegisterBooleanPref(kSuggestedContentInfoDismissedInLauncher,
+                                false);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 }  // namespace
@@ -2036,6 +2051,12 @@
   profile_prefs->ClearPref(kLoadCryptoTokenExtension);
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Added 10/2022.
+  profile_prefs->ClearPref(kSuggestedContentInfoShownInLauncher);
+  profile_prefs->ClearPref(kSuggestedContentInfoDismissedInLauncher);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/printing/printer_manager_dialog_mac.mm b/chrome/browser/printing/printer_manager_dialog_mac.cc
similarity index 61%
rename from chrome/browser/printing/printer_manager_dialog_mac.mm
rename to chrome/browser/printing/printer_manager_dialog_mac.cc
index 4c42c3c..2966452f 100644
--- a/chrome/browser/printing/printer_manager_dialog_mac.mm
+++ b/chrome/browser/printing/printer_manager_dialog_mac.cc
@@ -4,15 +4,13 @@
 
 #include "chrome/browser/printing/printer_manager_dialog.h"
 
-#import <AppKit/AppKit.h>
+#include "base/mac/mac_util.h"
 
 namespace printing {
 
-static NSString* kPrintAndFaxPrefPane =
-    @"/System/Library/PreferencePanes/PrintAndFax.prefPane";
-
 void PrinterManagerDialog::ShowPrinterManagerDialog() {
-  [[NSWorkspace sharedWorkspace] openFile:kPrintAndFaxPrefPane];
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kPrintersScanners);
 }
 
 }  // namespace printing
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.html b/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.html
index 417f722b..04e4a30 100644
--- a/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.html
+++ b/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.html
@@ -4,6 +4,7 @@
   }
 
   .subtitle {
+    font-family: var(--cros-font-family-google-sans);
     font-size: 24px;
     font-weight: 500;
     margin-top: 32px;
diff --git a/chrome/browser/resources/new_tab_page/modules/cart/module.html b/chrome/browser/resources/new_tab_page/modules/cart/module.html
index cc5049b..e698392 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart/module.html
+++ b/chrome/browser/resources/new_tab_page/modules/cart/module.html
@@ -179,7 +179,6 @@
     font-weight: 400;
     overflow: hidden;
     text-overflow: ellipsis;
-    text-shadow: var(--ntp-theme-text-shadow);
     white-space: nowrap;
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/display_and_magnification_page.ts b/chrome/browser/resources/settings/chromeos/os_a11y_page/display_and_magnification_page.ts
index a98472f..d30e058 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/display_and_magnification_page.ts
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/display_and_magnification_page.ts
@@ -148,6 +148,12 @@
     this.route_ = routes.A11Y_DISPLAY_AND_MAGNIFICATION;
   }
 
+  override ready() {
+    super.ready();
+
+    this.addFocusConfig(routes.DISPLAY, '#displaySubpageButton');
+  }
+
   /**
    * Note: Overrides RouteOriginBehavior implementation
    */
diff --git a/chrome/browser/safe_browsing/chrome_user_population_helper.cc b/chrome/browser/safe_browsing/chrome_user_population_helper.cc
index 825e2a9..d725926 100644
--- a/chrome/browser/safe_browsing/chrome_user_population_helper.cc
+++ b/chrome/browser/safe_browsing/chrome_user_population_helper.cc
@@ -47,20 +47,8 @@
   const absl::optional<ChromeUserPopulation>& cached_population =
       GetCachedUserPopulation(profile);
   if (!cached_population) {
-    base::UmaHistogramEnumeration("SafeBrowsing.NoCachedPopulationReason",
-                                  GetNoCachedPopulationReason(profile));
     return;
   }
-
-  base::UmaHistogramBoolean(
-      "SafeBrowsing.PopulationMatchesCachedValue.Population",
-      cached_population->user_population() == population.user_population());
-  base::UmaHistogramBoolean(
-      "SafeBrowsing.PopulationMatchesCachedValue.UserAgent",
-      cached_population->user_agent() == population.user_agent());
-  base::UmaHistogramBoolean(
-      "SafeBrowsing.PopulationMatchesCachedValue.Mbb",
-      cached_population->is_mbb_enabled() == population.is_mbb_enabled());
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 30e2d5a..acf41a30 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2064,6 +2064,8 @@
       "app_list/search/app_search_provider.h",
       "app_list/search/app_service_app_result.cc",
       "app_list/search/app_service_app_result.h",
+      "app_list/search/app_zero_state_provider.cc",
+      "app_list/search/app_zero_state_provider.h",
       "app_list/search/arc/arc_app_shortcut_search_result.cc",
       "app_list/search/arc/arc_app_shortcut_search_result.h",
       "app_list/search/arc/arc_app_shortcuts_search_provider.cc",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 92269ebc..6139280 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -706,6 +706,7 @@
         mStartSurfaceToolbarCoordinator.onStartSurfaceStateChanged(
                 newState, requestToShow, newLayoutType);
         updateToolbarLayoutVisibility();
+        updateButtonVisibility();
     }
 
     /**
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
index 8205bf4..268a2a7a 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionMediator.java
@@ -373,6 +373,13 @@
         properties.mIdpForDisplay = idpForDisplay;
         properties.mTermsOfServiceUrl = metadata.getTermsOfServiceUrl();
         properties.mPrivacyPolicyUrl = metadata.getPrivacyPolicyUrl();
+        properties.mTermsOfServiceClickRunnable = () -> {
+            RecordHistogram.recordBooleanHistogram(
+                    "Blink.FedCm.SignUp.TermsOfServiceClicked", true);
+        };
+        properties.mPrivacyPolicyClickRunnable = () -> {
+            RecordHistogram.recordBooleanHistogram("Blink.FedCm.SignUp.PrivacyPolicyClicked", true);
+        };
 
         return new PropertyModel.Builder(DataSharingConsentProperties.ALL_KEYS)
                 .with(DataSharingConsentProperties.PROPERTIES, properties)
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionProperties.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionProperties.java
index 4ff6a8d..c6b960ad 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionProperties.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionProperties.java
@@ -84,6 +84,8 @@
             public String mIdpForDisplay;
             public GURL mTermsOfServiceUrl;
             public GURL mPrivacyPolicyUrl;
+            public Runnable mTermsOfServiceClickRunnable;
+            public Runnable mPrivacyPolicyClickRunnable;
         }
 
         static final ReadableObjectPropertyKey<Properties> PROPERTIES =
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java
index edec4f3..8f10bfc 100644
--- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java
+++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewBinder.java
@@ -143,13 +143,15 @@
         }
     }
 
-    static SpanApplier.SpanInfo createLink(Context context, GURL url, String tag) {
+    static SpanApplier.SpanInfo createLink(
+            Context context, String tag, GURL url, Runnable clickRunnable) {
         if (GURL.isEmptyOrInvalid(url)) return null;
 
         String startTag = "<" + tag + ">";
         String endTag = "</" + tag + ">";
         Callback<View> onClickCallback = v -> {
             CustomTabActivity.showInfoPage(context, url.getSpec());
+            clickRunnable.run();
         };
         return new SpanApplier.SpanInfo(
                 startTag, endTag, new NoUnderlineClickableSpan(context, onClickCallback));
@@ -167,10 +169,10 @@
                     model.get(DataSharingConsentProperties.PROPERTIES);
 
             Context context = view.getContext();
-            SpanApplier.SpanInfo privacyPolicySpan =
-                    createLink(context, properties.mPrivacyPolicyUrl, "link_privacy_policy");
-            SpanApplier.SpanInfo termsOfServiceSpan =
-                    createLink(context, properties.mTermsOfServiceUrl, "link_terms_of_service");
+            SpanApplier.SpanInfo privacyPolicySpan = createLink(context, "link_privacy_policy",
+                    properties.mPrivacyPolicyUrl, properties.mPrivacyPolicyClickRunnable);
+            SpanApplier.SpanInfo termsOfServiceSpan = createLink(context, "link_terms_of_service",
+                    properties.mTermsOfServiceUrl, properties.mTermsOfServiceClickRunnable);
 
             int consentTextId;
             if (privacyPolicySpan == null && termsOfServiceSpan == null) {
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index a83365b4..69d5a39b 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -37,13 +37,8 @@
 
 }  // namespace
 
-AppSearchProvider::AppSearchProvider(Profile* profile,
-                                     AppListControllerDelegate* list_controller,
-                                     base::Clock* clock,
-                                     AppListModelUpdater* model_updater)
-    : model_updater_(model_updater) {
-  data_source_ =
-      std::make_unique<AppSearchDataSource>(profile, list_controller, clock);
+AppSearchProvider::AppSearchProvider(AppSearchDataSource* data_source)
+    : data_source_(data_source) {
   app_updates_subscription_ =
       data_source_->SubscribeToAppUpdates(base::BindRepeating(
           &AppSearchProvider::UpdateResults, base::Unretained(this)));
@@ -70,35 +65,17 @@
 
 void AppSearchProvider::StartZeroState() {
   query_.clear();
-  query_start_time_ = base::TimeTicks::Now();
-  record_query_uma_ = true;
-
-  {
-    // Prevent `UpdateResults()` from running as a result of a data source
-    // refresh callback to avoid double update.
-    base::AutoReset<bool> auto_reset(&updates_blocked_, true);
-    data_source_->RefreshIfNeeded();
-  }
-
-  UpdateResults();
+  record_query_uma_ = false;
 }
 
 ash::AppListSearchResultType AppSearchProvider::ResultType() const {
   return ash::AppListSearchResultType::kInstalledApp;
 }
 
-bool AppSearchProvider::ShouldBlockZeroState() const {
-  return true;
-}
+void AppSearchProvider::UpdateResults() {
+  if (updates_blocked_)
+    return;
 
-void AppSearchProvider::UpdateRecommendedResults(
-    const base::flat_map<std::string, uint16_t>& id_to_app_list_index) {
-  SearchProvider::Results new_results =
-      data_source_->GetRecommendations(id_to_app_list_index);
-  PublishQueriedResultsOrRecommendation(false, &new_results);
-}
-
-void AppSearchProvider::UpdateQueriedResults() {
   SearchProvider::Results new_results;
 
   const bool use_exact_match =
@@ -111,48 +88,13 @@
     new_results = data_source_->GetFuzzyMatches(query_);
   }
 
-  PublishQueriedResultsOrRecommendation(true, &new_results);
-}
-
-void AppSearchProvider::PublishQueriedResultsOrRecommendation(
-    bool is_queried_search,
-    Results* new_results) {
-  MaybeRecordQueryLatencyHistogram(is_queried_search);
-  SwapResults(new_results);
-}
-
-void AppSearchProvider::MaybeRecordQueryLatencyHistogram(
-    bool is_queried_search) {
-  // Record the query latency only if search provider is queried by user
-  // initiating a search or getting zero state suggestions.
-  if (!record_query_uma_)
-    return;
-
-  if (is_queried_search) {
+  if (record_query_uma_) {
+    record_query_uma_ = false;
     UMA_HISTOGRAM_TIMES("Apps.AppList.AppSearchProvider.QueryTime",
                         base::TimeTicks::Now() - query_start_time_);
-  } else {
-    UMA_HISTOGRAM_TIMES("Apps.AppList.AppSearchProvider.ZeroStateLatency",
-                        base::TimeTicks::Now() - query_start_time_);
   }
-  record_query_uma_ = false;
-}
 
-void AppSearchProvider::UpdateResults() {
-  if (updates_blocked_)
-    return;
-
-  const bool show_recommendations = query_.empty();
-
-  if (show_recommendations) {
-    // Get the map of app ids to their position in the app list, and then
-    // update results.
-    // Unretained is safe because the callback gets called synchronously.
-    model_updater_->GetIdToAppListIndexMap(base::BindOnce(
-        &AppSearchProvider::UpdateRecommendedResults, base::Unretained(this)));
-  } else {
-    UpdateQueriedResults();
-  }
+  SwapResults(&new_results);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.h b/chrome/browser/ui/app_list/search/app_search_provider.h
index 2e62129..6ebd29c 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.h
+++ b/chrome/browser/ui/app_list/search/app_search_provider.h
@@ -9,32 +9,16 @@
 #include <string>
 
 #include "base/callback_list.h"
-#include "base/containers/flat_map.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 
-class AppListControllerDelegate;
-class AppListModelUpdater;
-class Profile;
-
-namespace base {
-class Clock;
-}
-
 namespace app_list {
 
 class AppSearchDataSource;
 
 class AppSearchProvider : public SearchProvider {
  public:
-  // |clock| should be used by tests that needs to overrides the time.
-  // Otherwise, pass a base::DefaultClock instance. This doesn't take the
-  // ownership of the clock. |clock| must outlive the AppSearchProvider
-  // instance.
-  AppSearchProvider(Profile* profile,
-                    AppListControllerDelegate* list_controller,
-                    base::Clock* clock,
-                    AppListModelUpdater* model_updater);
+  explicit AppSearchProvider(AppSearchDataSource* data_source);
 
   AppSearchProvider(const AppSearchProvider&) = delete;
   AppSearchProvider& operator=(const AppSearchProvider&) = delete;
@@ -45,33 +29,15 @@
   void Start(const std::u16string& query) override;
   void StartZeroState() override;
   ash::AppListSearchResultType ResultType() const override;
-  bool ShouldBlockZeroState() const override;
 
  private:
   void UpdateResults();
 
-  // Updates the zero-state app recommendations ("recent apps").
-  void UpdateRecommendedResults(
-      const base::flat_map<std::string, uint16_t>& id_to_app_list_index);
-
-  void UpdateQueriedResults();
-
-  // Publishes either the queried results or recommendation.
-  // |is_queried_search|: true for queried results, false for recommendation.
-  void PublishQueriedResultsOrRecommendation(bool is_queried_search,
-                                             Results* new_results);
-
-  // Records the app search provider's latency when user initiates a search or
-  // gets the zero state suggestions.
-  // If |is_queried_search| is true, record query latency; otherwise, record
-  // zero state recommendation latency.
-  void MaybeRecordQueryLatencyHistogram(bool is_queried_search);
+  AppSearchDataSource* const data_source_;
 
   std::u16string query_;
   base::TimeTicks query_start_time_;
   bool record_query_uma_ = false;
-  AppListModelUpdater* const model_updater_;
-  std::unique_ptr<AppSearchDataSource> data_source_;
 
   // Used to skip result updates caused by data source changes due to an
   // explicit refresh request.
diff --git a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
index e055cbb..d96d028 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
@@ -29,6 +29,8 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/arc/arc_default_app_list.h"
+#include "chrome/browser/ui/app_list/search/app_search_data_source.h"
+#include "chrome/browser/ui/app_list/search/app_zero_state_provider.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/test/test_search_controller.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
@@ -121,14 +123,16 @@
 
 }  // namespace
 
-class AppSearchProviderTest : public AppListTestBase {
+class AppSearchProviderTestBase : public AppListTestBase {
  public:
-  AppSearchProviderTest() = default;
+  explicit AppSearchProviderTestBase(bool zero_state_provider)
+      : zero_state_provider_(zero_state_provider) {}
 
-  AppSearchProviderTest(const AppSearchProviderTest&) = delete;
-  AppSearchProviderTest& operator=(const AppSearchProviderTest&) = delete;
+  AppSearchProviderTestBase(const AppSearchProviderTestBase&) = delete;
+  AppSearchProviderTestBase& operator=(const AppSearchProviderTestBase&) =
+      delete;
 
-  ~AppSearchProviderTest() override {}
+  ~AppSearchProviderTestBase() override = default;
 
   // AppListTestBase overrides:
   void SetUp() override {
@@ -142,19 +146,34 @@
 
   void CreateSearch() {
     search_controller_ = std::make_unique<TestSearchController>();
-    auto app_search = std::make_unique<AppSearchProvider>(
-        profile_.get(), nullptr, &clock_, model_updater_.get());
+    data_source_ =
+        std::make_unique<AppSearchDataSource>(profile_.get(), nullptr, &clock_);
+
+    std::unique_ptr<SearchProvider> app_search;
+    if (zero_state_provider_) {
+      app_search = std::make_unique<AppZeroStateProvider>(data_source_.get(),
+                                                          model_updater_.get());
+    } else {
+      app_search = std::make_unique<AppSearchProvider>(data_source_.get());
+    }
+
     app_search_ = app_search.get();
+
     search_controller_->AddProvider(0, std::move(app_search));
   }
 
   std::string RunQuery(const std::string& query) {
-    if (query.empty()) {
-      search_controller_->StartZeroState(base::DoNothing(), base::TimeDelta());
-    } else {
-      search_controller_->StartSearch(base::UTF8ToUTF16(query));
-    }
+    EXPECT_FALSE(query.empty());
+    search_controller_->StartSearch(base::UTF8ToUTF16(query));
+    return GetSortedResultsString();
+  }
 
+  std::string RunZeroStateSearch() {
+    search_controller_->StartZeroState(base::DoNothing(), base::TimeDelta());
+    return GetSortedResultsString();
+  }
+
+  std::string GetSortedResultsString() {
     // Sort results by relevance.
     std::vector<ChromeSearchResult*> sorted_results;
     for (const auto& result : results())
@@ -231,15 +250,37 @@
   void CallViewClosing() { app_search_->ViewClosing(); }
 
  private:
+  // Whether the test is testing zero state, or queried apps search provider.
+  const bool zero_state_provider_;
+
   base::SimpleTestClock clock_;
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<FakeAppListModelUpdater> model_updater_;
   std::unique_ptr<TestSearchController> search_controller_;
-  AppSearchProvider* app_search_ = nullptr;
+  std::unique_ptr<AppSearchDataSource> data_source_;
+  SearchProvider* app_search_ = nullptr;
   std::unique_ptr<::test::TestAppListControllerDelegate> controller_;
   ArcAppTest arc_test_;
 };
 
+class AppSearchProviderTest : public AppSearchProviderTestBase {
+ public:
+  AppSearchProviderTest()
+      : AppSearchProviderTestBase(/*zero_state_provider=*/false) {}
+  AppSearchProviderTest(const AppSearchProviderTest&) = delete;
+  AppSearchProviderTest& operator=(const AppSearchProviderTest&) = delete;
+  ~AppSearchProviderTest() override = default;
+};
+
+class AppZeroStateProviderTest : public AppSearchProviderTestBase {
+ public:
+  AppZeroStateProviderTest()
+      : AppSearchProviderTestBase(/*zero_state_provider=*/true) {}
+  AppZeroStateProviderTest(const AppZeroStateProviderTest&) = delete;
+  AppZeroStateProviderTest& operator=(const AppZeroStateProviderTest&) = delete;
+  ~AppZeroStateProviderTest() override = default;
+};
+
 TEST_F(AppSearchProviderTest, Basic) {
   arc_test().SetUp(profile());
   std::vector<arc::mojom::AppInfoPtr> arc_apps;
@@ -399,7 +440,7 @@
   arc_test().TearDown();
 }
 
-TEST_F(AppSearchProviderTest, FetchRecommendations) {
+TEST_F(AppZeroStateProviderTest, FetchRecommendations) {
   CreateSearch();
 
   extensions::ExtensionPrefs* prefs =
@@ -410,14 +451,14 @@
   prefs->SetLastLaunchTime(kPackagedApp2Id, MicrosecondsSinceEpoch(5));
   // Allow async callbacks to run.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery(""));
+  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunZeroStateSearch());
 
   prefs->SetLastLaunchTime(kHostedAppId, MicrosecondsSinceEpoch(5));
   prefs->SetLastLaunchTime(kPackagedApp1Id, MicrosecondsSinceEpoch(10));
   prefs->SetLastLaunchTime(kPackagedApp2Id, MicrosecondsSinceEpoch(20));
   // Allow async callbacks to run.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ("Packaged App 2,Packaged App 1,Hosted App", RunQuery(""));
+  EXPECT_EQ("Packaged App 2,Packaged App 1,Hosted App", RunZeroStateSearch());
 
   // Times in the future should just be handled as highest priority.
   prefs->SetLastLaunchTime(kHostedAppId, base::Time::Now() + base::Seconds(5));
@@ -425,10 +466,15 @@
   prefs->SetLastLaunchTime(kPackagedApp2Id, MicrosecondsSinceEpoch(5));
   // Allow async callbacks to run.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery(""));
+  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunZeroStateSearch());
+
+  // Validate that queried search does not clear out zero state results.
+  RunQuery("No matches");
+  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2",
+            GetSortedResultsString());
 }
 
-TEST_F(AppSearchProviderTest, DefaultRecommendedAppRanking) {
+TEST_F(AppZeroStateProviderTest, DefaultRecommendedAppRanking) {
   // Disable the pre-installed high-priority extensions. This test simulates
   // a brand new profile being added to a device, and should not include these.
   service_->UninstallExtension(
@@ -472,7 +518,7 @@
   base::RunLoop().RunUntilIdle();
   CreateSearch();
 
-  EXPECT_EQ("OsSettings,Help,Canvas,Camera", RunQuery(""));
+  EXPECT_EQ("OsSettings,Help,Canvas,Camera", RunZeroStateSearch());
 
   // Install a normal (non-default-installed) app.
   const std::string normal_app_id =
@@ -492,7 +538,7 @@
   CreateSearch();
   EXPECT_EQ(
       std::string(kRankingNormalAppName) + ",OsSettings,Help,Canvas,Camera",
-      RunQuery(""));
+      RunZeroStateSearch());
 
   // Simulate launching one of the default apps. Expect that this brings it to
   // higher precedence than all the others.
@@ -500,10 +546,10 @@
   CreateSearch();
   EXPECT_EQ("Canvas," + std::string(kRankingNormalAppName) +
                 ",OsSettings,Help,Camera",
-            RunQuery(""));
+            RunZeroStateSearch());
 }
 
-TEST_F(AppSearchProviderTest, FetchUnlaunchedRecommendations) {
+TEST_F(AppZeroStateProviderTest, FetchUnlaunchedRecommendations) {
   CreateSearch();
 
   extensions::ExtensionPrefs* prefs =
@@ -514,7 +560,7 @@
   prefs->SetLastLaunchTime(kHostedAppId, base::Time::Now());
   prefs->SetLastLaunchTime(kPackagedApp1Id, MicrosecondsSinceEpoch(0));
   prefs->SetLastLaunchTime(kPackagedApp2Id, MicrosecondsSinceEpoch(0));
-  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery(""));
+  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunZeroStateSearch());
 }
 
 TEST_F(AppSearchProviderTest, FilterDuplicate) {
@@ -723,28 +769,23 @@
   EXPECT_EQ(kKeyboardShortcutHelperInternalName, RunQuery("Helper"));
 }
 
-enum class TestExtensionInstallType {
-  CONTROLLED_BY_POLICY,
-  CHROME_COMPONENT,
-  INSTALLED_BY_DEFAULT,
-  INSTALLED_BY_OEM,
-};
-
-class AppSearchProviderWithExtensionInstallType
-    : public AppSearchProviderTest,
-      public ::testing::WithParamInterface<TestExtensionInstallType> {
+class AppSearchProviderOemAppTest
+    : public AppSearchProviderTestBase,
+      public ::testing::WithParamInterface</*test_zero_state_search=*/bool> {
  public:
-  AppSearchProviderWithExtensionInstallType() = default;
+  AppSearchProviderOemAppTest()
+      : AppSearchProviderTestBase(test_zero_state_search()) {}
 
-  AppSearchProviderWithExtensionInstallType(
-      const AppSearchProviderWithExtensionInstallType&) = delete;
-  AppSearchProviderWithExtensionInstallType& operator=(
-      const AppSearchProviderWithExtensionInstallType&) = delete;
+  AppSearchProviderOemAppTest(const AppSearchProviderOemAppTest&) = delete;
+  AppSearchProviderOemAppTest& operator=(const AppSearchProviderOemAppTest&) =
+      delete;
 
-  ~AppSearchProviderWithExtensionInstallType() override = default;
+  ~AppSearchProviderOemAppTest() override = default;
+
+  bool test_zero_state_search() const { return GetParam(); }
 };
 
-TEST_P(AppSearchProviderWithExtensionInstallType, OemResultsOnFirstBoot) {
+TEST_P(AppSearchProviderOemAppTest, OemResultsOnFirstBoot) {
   // Disable the pre-installed high-priority extensions. This test simulates
   // a brand new profile being added to a device, and should not include these.
   service_->UninstallExtension(
@@ -784,8 +825,10 @@
   base::RunLoop().RunUntilIdle();
   CreateSearch();
 
+  std::string results_string =
+      test_zero_state_search() ? RunZeroStateSearch() : RunQuery("Oem");
   std::vector<std::string> results = base::SplitString(
-      RunQuery(""), ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+      results_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
 
   for (auto* app : kOemAppNames) {
     EXPECT_TRUE(base::Contains(results, app));
@@ -879,13 +922,7 @@
   arc_test().TearDown();
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    AppSearchProviderWithExtensionInstallType,
-    ::testing::ValuesIn({TestExtensionInstallType::CONTROLLED_BY_POLICY,
-                         TestExtensionInstallType::CHROME_COMPONENT,
-                         TestExtensionInstallType::INSTALLED_BY_DEFAULT,
-                         TestExtensionInstallType::INSTALLED_BY_OEM}));
+INSTANTIATE_TEST_SUITE_P(All, AppSearchProviderOemAppTest, ::testing::Bool());
 
 INSTANTIATE_TEST_SUITE_P(
     All,
diff --git a/chrome/browser/ui/app_list/search/app_zero_state_provider.cc b/chrome/browser/ui/app_list/search/app_zero_state_provider.cc
new file mode 100644
index 0000000..6439c8b
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/app_zero_state_provider.cc
@@ -0,0 +1,63 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/app_zero_state_provider.h"
+
+#include <string>
+#include <utility>
+
+#include "ash/public/cpp/app_list/app_list_features.h"
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/search/app_search_data_source.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+
+namespace app_list {
+
+AppZeroStateProvider::AppZeroStateProvider(AppSearchDataSource* data_source,
+                                           AppListModelUpdater* model_updater)
+    : data_source_(data_source), model_updater_(model_updater) {
+  // NOTE: Unlike AppSearchProvider, AppZeroStateProvider does not have to
+  // update search model when app status, or other app information changes. The
+  // recent apps UI implementation updates app representations independently of
+  // search model, using app list model directly. The UI only uses search model
+  // to determine preferred app display order - updating search model may change
+  // order of apps, which would be undesirable UI behavior (it could be
+  // perceived as pop-in after app list has been shown).
+  // If the UI behavior changes, the decision not to update search model for
+  // recent apps whenever app service state changes should be reevaluated.
+}
+
+AppZeroStateProvider::~AppZeroStateProvider() = default;
+
+void AppZeroStateProvider::StartZeroState() {
+  data_source_->RefreshIfNeeded();
+  UpdateResults();
+}
+
+ash::AppListSearchResultType AppZeroStateProvider::ResultType() const {
+  return ash::AppListSearchResultType::kZeroStateApp;
+}
+
+void AppZeroStateProvider::UpdateRecommendedResults(
+    const base::flat_map<std::string, uint16_t>& id_to_app_list_index) {
+  SearchProvider::Results new_results =
+      data_source_->GetRecommendations(id_to_app_list_index);
+  UMA_HISTOGRAM_TIMES("Apps.AppList.AppSearchProvider.ZeroStateLatency",
+                      base::TimeTicks::Now() - query_start_time_);
+
+  SwapResults(&new_results);
+}
+
+void AppZeroStateProvider::UpdateResults() {
+  // Get the map of app ids to their position in the app list, and then
+  // update results.
+  // Unretained is safe because the callback gets called synchronously.
+  model_updater_->GetIdToAppListIndexMap(base::BindOnce(
+      &AppZeroStateProvider::UpdateRecommendedResults, base::Unretained(this)));
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/app_zero_state_provider.h b/chrome/browser/ui/app_list/search/app_zero_state_provider.h
new file mode 100644
index 0000000..1fa0762
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/app_zero_state_provider.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_APP_ZERO_STATE_PROVIDER_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_APP_ZERO_STATE_PROVIDER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/ui/app_list/search/search_provider.h"
+
+class AppListModelUpdater;
+
+namespace app_list {
+
+class AppSearchDataSource;
+
+class AppZeroStateProvider : public SearchProvider {
+ public:
+  AppZeroStateProvider(AppSearchDataSource* data_source,
+                       AppListModelUpdater* model_updater);
+
+  AppZeroStateProvider(const AppZeroStateProvider&) = delete;
+  AppZeroStateProvider& operator=(const AppZeroStateProvider&) = delete;
+
+  ~AppZeroStateProvider() override;
+
+  // SearchProvider overrides:
+  void StartZeroState() override;
+  ash::AppListSearchResultType ResultType() const override;
+
+ private:
+  void UpdateResults();
+
+  // Updates the zero-state app recommendations ("recent apps").
+  void UpdateRecommendedResults(
+      const base::flat_map<std::string, uint16_t>& id_to_app_list_index);
+
+  AppSearchDataSource* const data_source_;
+  AppListModelUpdater* const model_updater_;
+
+  base::TimeTicks query_start_time_;
+
+  base::WeakPtrFactory<AppZeroStateProvider> weak_ptr_factory_{this};
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_APP_ZERO_STATE_PROVIDER_H_
diff --git a/chrome/browser/ui/app_list/search/common/types_util.cc b/chrome/browser/ui/app_list/search/common/types_util.cc
index 1dacd9c3..56a3a96 100644
--- a/chrome/browser/ui/app_list/search/common/types_util.cc
+++ b/chrome/browser/ui/app_list/search/common/types_util.cc
@@ -14,6 +14,8 @@
       return "Unknown";
     case ash::AppListSearchResultType::kInstalledApp:
       return "Installed app";
+    case ash::AppListSearchResultType::kZeroStateApp:
+      return "Zero state app";
     case ash::AppListSearchResultType::kPlayStoreApp:
       return "Play store app";
     case ash::AppListSearchResultType::kInstantApp:
diff --git a/chrome/browser/ui/app_list/search/files/file_result_unittest.cc b/chrome/browser/ui/app_list/search/files/file_result_unittest.cc
index 718e0339..030bbe5 100644
--- a/chrome/browser/ui/app_list/search/files/file_result_unittest.cc
+++ b/chrome/browser/ui/app_list/search/files/file_result_unittest.cc
@@ -7,18 +7,20 @@
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/test/test_app_list_color_provider.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/common/icon_constants.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/string_matching/tokenized_string.h"
+#include "chromeos/ui/base/file_icon_util.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/skia_util.h"
 
 namespace app_list {
 
@@ -97,4 +99,52 @@
   }
 }
 
+TEST_F(FileResultTest, Icons) {
+  const base::FilePath chipPath("/my/test/file.Pdf");
+  FileResult chipResult(
+      /*id=*/"zero_state_file://" + chipPath.value(), chipPath, u"some details",
+      ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kChip, 0.2f, std::u16string(),
+      FileResult::Type::kFile, profile_.get());
+  EXPECT_FALSE(chipResult.chip_icon().isNull());
+  EXPECT_TRUE(chipResult.icon().icon.isNull());
+
+  const base::FilePath excelPath("/my/test/mySheet.xlsx");
+  FileResult fileResult(
+      /*id=*/"zero_state_file://" + excelPath.value(), excelPath,
+      u"some details", ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
+      FileResult::Type::kFile, profile_.get());
+  EXPECT_TRUE(fileResult.chip_icon().isNull());
+  EXPECT_FALSE(fileResult.icon().icon.isNull());
+  EXPECT_EQ(fileResult.icon().dimension, kSystemIconDimension);
+  EXPECT_TRUE(gfx::BitmapsAreEqual(
+      *fileResult.icon().icon.bitmap(),
+      *chromeos::GetIconForPath(excelPath, false).bitmap()));
+
+  const base::FilePath folderPath("my/Maps");
+  FileResult folderResult(
+      /*id=*/"zero_state_file://" + folderPath.value(), folderPath,
+      absl::nullopt, ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
+      FileResult::Type::kDirectory, profile_.get());
+  EXPECT_TRUE(folderResult.chip_icon().isNull());
+  EXPECT_EQ(folderResult.icon().dimension, kSystemIconDimension);
+  EXPECT_TRUE(gfx::BitmapsAreEqual(
+      *folderResult.icon().icon.bitmap(),
+      *chromeos::GetIconFromType("folder", false).bitmap()));
+
+  const base::FilePath sharedPath("my/Shared");
+  FileResult sharedResult(
+      /*id=*/"zero_state_file://" + sharedPath.value(), sharedPath,
+      absl::nullopt, ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
+      FileResult::Type::kSharedDirectory, profile_.get());
+  EXPECT_TRUE(sharedResult.chip_icon().isNull());
+  EXPECT_EQ(sharedResult.icon().dimension, kSystemIconDimension);
+  EXPECT_TRUE(gfx::BitmapsAreEqual(
+      *sharedResult.icon().icon.bitmap(),
+      *chromeos::GetIconFromType("shared", false).bitmap()));
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
index fe18e240..8e0f3df 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
@@ -156,10 +156,6 @@
   return ash::AppListSearchResultType::kZeroStateDrive;
 }
 
-bool ZeroStateDriveProvider::ShouldBlockZeroState() const {
-  return true;
-}
-
 void ZeroStateDriveProvider::StartZeroState() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
index 11dd2d24..20b83d6b 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
@@ -60,7 +60,6 @@
   void StartZeroState() override;
   void ViewClosing() override;
   ash::AppListSearchResultType ResultType() const override;
-  bool ShouldBlockZeroState() const override;
 
  private:
   // Called when file suggestion data are fetched from the service.
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
index 807f1f0b..369e526 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
@@ -78,10 +78,6 @@
   return ash::AppListSearchResultType::kZeroStateFile;
 }
 
-bool ZeroStateFileProvider::ShouldBlockZeroState() const {
-  return true;
-}
-
 void ZeroStateFileProvider::StartZeroState() {
   query_start_time_ = base::TimeTicks::Now();
 
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.h b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.h
index 33dc681..6e05f1a1 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.h
+++ b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.h
@@ -41,7 +41,6 @@
   // SearchProvider:
   void StartZeroState() override;
   ash::AppListSearchResultType ResultType() const override;
-  bool ShouldBlockZeroState() const override;
 
  private:
   // Called when file suggestion data are fetched from the service.
diff --git a/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc b/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc
index 7fae2cb..8db26f3 100644
--- a/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc
+++ b/chrome/browser/ui/app_list/search/help_app_search_browsertest.cc
@@ -37,8 +37,10 @@
 class HelpAppSearchBrowserTestBase : public AppListSearchBrowserTest {
  public:
   HelpAppSearchBrowserTestBase() {
-    scoped_feature_list_.InitAndEnableFeature(
-        chromeos::features::kHelpAppLauncherSearch);
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {{ash::features::kProductivityLauncher, {{"enable_continue", "true"}}},
+         {{chromeos::features::kHelpAppLauncherSearch}, {}}},
+        {});
   }
 
   ~HelpAppSearchBrowserTestBase() override = default;
@@ -302,7 +304,7 @@
 
   ShowAppListAndWaitForZeroStateResults(
       {ash::AppListSearchResultType::kZeroStateHelpApp,
-       ash::AppListSearchResultType::kInstalledApp});
+       ash::AppListSearchResultType::kZeroStateApp});
 
   auto* result = FindResult(web_app::kHelpAppId);
   ASSERT_TRUE(result);
@@ -319,7 +321,7 @@
 
   ShowAppListAndWaitForZeroStateResults(
       {ash::AppListSearchResultType::kZeroStateHelpApp,
-       ash::AppListSearchResultType::kInstalledApp});
+       ash::AppListSearchResultType::kZeroStateApp});
 
   auto* result = FindResult(web_app::kHelpAppId);
   ASSERT_TRUE(result);
diff --git a/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc b/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc
index dda0a297..4b7729bd 100644
--- a/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc
+++ b/chrome/browser/ui/app_list/search/help_app_zero_state_provider.cc
@@ -192,10 +192,6 @@
   return ash::AppListSearchResultType::kZeroStateHelpApp;
 }
 
-bool HelpAppZeroStateProvider::ShouldBlockZeroState() const {
-  return true;
-}
-
 void HelpAppZeroStateProvider::OnAppUpdate(const apps::AppUpdate& update) {
   if (update.AppId() == web_app::kHelpAppId && update.ReadinessChanged() &&
       update.Readiness() == apps::Readiness::kReady) {
diff --git a/chrome/browser/ui/app_list/search/help_app_zero_state_provider.h b/chrome/browser/ui/app_list/search/help_app_zero_state_provider.h
index 3ec615d..de2a9fa 100644
--- a/chrome/browser/ui/app_list/search/help_app_zero_state_provider.h
+++ b/chrome/browser/ui/app_list/search/help_app_zero_state_provider.h
@@ -61,7 +61,6 @@
   // SearchProvider:
   void StartZeroState() override;
   ash::AppListSearchResultType ResultType() const override;
-  bool ShouldBlockZeroState() const override;
 
   // apps::AppRegistryCache::Observer:
   void OnAppUpdate(const apps::AppUpdate& update) override;
diff --git a/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc b/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
index 38e23b2a..6d7c4aaf 100644
--- a/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
+++ b/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
@@ -22,6 +22,7 @@
 bool ShouldIgnoreProvider(ProviderType type) {
   switch (type) {
       // Continue providers:
+    case ProviderType::kZeroStateApp:
     case ProviderType::kZeroStateFile:
     case ProviderType::kZeroStateDrive:
       // Low-intent providers:
diff --git a/chrome/browser/ui/app_list/search/ranking/ranking_item_util.cc b/chrome/browser/ui/app_list/search/ranking/ranking_item_util.cc
index e16a3b8..c224fb3e 100644
--- a/chrome/browser/ui/app_list/search/ranking/ranking_item_util.cc
+++ b/chrome/browser/ui/app_list/search/ranking/ranking_item_util.cc
@@ -18,6 +18,7 @@
     const ChromeSearchResult& result) {
   switch (result.result_type()) {
     case ash::AppListSearchResultType::kInstalledApp:
+    case ash::AppListSearchResultType::kZeroStateApp:
     case ash::AppListSearchResultType::kInternalApp:
     case ash::AppListSearchResultType::kGames:
       return RankingItemType::kApp;
diff --git a/chrome/browser/ui/app_list/search/ranking/util.cc b/chrome/browser/ui/app_list/search/ranking/util.cc
index 5c96310c..7c4d4701 100644
--- a/chrome/browser/ui/app_list/search/ranking/util.cc
+++ b/chrome/browser/ui/app_list/search/ranking/util.cc
@@ -32,6 +32,7 @@
 Category ResultTypeToCategory(ResultType result_type) {
   switch (result_type) {
     case ResultType::kInstalledApp:
+    case ResultType::kZeroStateApp:
     case ResultType::kInstantApp:
     case ResultType::kInternalApp:
     case ResultType::kGames:
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index 02ad31d9..c901473 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -32,6 +32,7 @@
 
 namespace app_list {
 
+class AppSearchDataSource;
 class SearchProvider;
 enum class RankingItemType;
 
@@ -79,6 +80,10 @@
   virtual void InvokeResultAction(ChromeSearchResult* result,
                                   ash::SearchResultActionType action) = 0;
 
+  // Returns AppSearchDataSource instance that should be used with app search
+  // providers.
+  virtual AppSearchDataSource* GetAppSearchDataSource() = 0;
+
   // Adds a new mixer group. See Mixer::AddGroup.
   virtual size_t AddGroup(size_t max_results) = 0;
 
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 7bd9555..4da8ec7e 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/app_search_provider.h"
+#include "chrome/browser/ui/app_list/search/app_zero_state_provider.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.h"
 #include "chrome/browser/ui/app_list/search/assistant_text_search_provider.h"
@@ -99,10 +100,12 @@
       ash::SharedAppListConfig::instance().max_search_result_list_items());
 
   // Add search providers.
+  controller->AddProvider(apps_group_id,
+                          std::make_unique<AppSearchProvider>(
+                              controller->GetAppSearchDataSource()));
   controller->AddProvider(
-      apps_group_id, std::make_unique<AppSearchProvider>(
-                         profile, list_controller,
-                         base::DefaultClock::GetInstance(), model_updater));
+      apps_group_id, std::make_unique<AppZeroStateProvider>(
+                         controller->GetAppSearchDataSource(), model_updater));
 
   if (crosapi::browser_util::IsLacrosEnabled()) {
     controller->AddProvider(
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.cc b/chrome/browser/ui/app_list/search/search_controller_impl.cc
index 0f6a5519d..a1a2f25 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.cc
@@ -16,10 +16,12 @@
 #include "base/metrics/metrics_hashes.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/search/app_search_data_source.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/common/string_util.h"
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
@@ -41,6 +43,10 @@
       mixer_(std::make_unique<Mixer>(model_updater, this)),
       metrics_observer_(
           std::make_unique<SearchMetricsManager>(profile, notifier)),
+      app_search_data_source_(std::make_unique<AppSearchDataSource>(
+          profile,
+          list_controller,
+          base::DefaultClock::GetInstance())),
       list_controller_(list_controller),
       notifier_(notifier) {
   if (notifier_)
@@ -117,6 +123,10 @@
   result->InvokeAction(action);
 }
 
+AppSearchDataSource* SearchControllerImpl::GetAppSearchDataSource() {
+  return app_search_data_source_.get();
+}
+
 size_t SearchControllerImpl::AddGroup(size_t max_results) {
   return mixer_->AddGroup(max_results);
 }
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.h b/chrome/browser/ui/app_list/search/search_controller_impl.h
index 701540d..1e0b82d 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl.h
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.h
@@ -62,6 +62,7 @@
   void OpenResult(ChromeSearchResult* result, int event_flags) override;
   void InvokeResultAction(ChromeSearchResult* result,
                           ash::SearchResultActionType action) override;
+  AppSearchDataSource* GetAppSearchDataSource() override;
   size_t AddGroup(size_t max_results) override;
   void AddProvider(size_t group_id,
                    std::unique_ptr<SearchProvider> provider) override;
@@ -112,6 +113,7 @@
 
   std::unique_ptr<Mixer> mixer_;
   std::unique_ptr<SearchMetricsManager> metrics_observer_;
+  std::unique_ptr<AppSearchDataSource> app_search_data_source_;
   using Providers = std::vector<std::unique_ptr<SearchProvider>>;
   Providers providers_;
   AppListControllerDelegate* list_controller_;
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
index d44e1a8..23364f2f4 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
@@ -15,10 +15,12 @@
 #include "base/metrics/metrics_hashes.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/search/app_search_data_source.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/common/string_util.h"
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
@@ -34,9 +36,9 @@
 namespace app_list {
 namespace {
 
-void ClearAllResultsExceptContinue(ResultsMap& results) {
+void ClearNonZeroStateResults(ResultsMap& results) {
   for (auto it = results.begin(); it != results.end();) {
-    if (!ash::IsContinueSectionResultType(it->first)) {
+    if (!ash::IsZeroStateResultType(it->first)) {
       it = results.erase(it);
     } else {
       ++it;
@@ -58,6 +60,10 @@
       ranker_(std::make_unique<RankerDelegate>(profile, this)),
       metrics_manager_(
           std::make_unique<SearchMetricsManager>(profile, notifier)),
+      app_search_data_source_(std::make_unique<AppSearchDataSource>(
+          profile,
+          list_controller,
+          base::DefaultClock::GetInstance())),
       model_updater_(model_updater),
       list_controller_(list_controller) {}
 
@@ -85,7 +91,7 @@
   //
   // b) were in search query: do not publish these changes, so that the
   //    old results stay on screen until the new ones are ready.
-  ClearAllResultsExceptContinue(results_);
+  ClearNonZeroStateResults(results_);
   if (last_query_.empty())
     Publish();
 
@@ -189,6 +195,10 @@
   }
 }
 
+AppSearchDataSource* SearchControllerImplNew::GetAppSearchDataSource() {
+  return app_search_data_source_.get();
+}
+
 size_t SearchControllerImplNew::AddGroup(size_t max_results) {
   // Unused.
   return 0ul;
@@ -197,7 +207,7 @@
 void SearchControllerImplNew::AddProvider(
     size_t group_id,
     std::unique_ptr<SearchProvider> provider) {
-  if (provider->ShouldBlockZeroState())
+  if (ash::IsZeroStateResultType(provider->ResultType()))
     ++total_zero_state_blockers_;
   provider->set_controller(this);
   provider->set_result_changed_callback(
@@ -240,7 +250,7 @@
     const SearchProvider* provider) {
   Rank(provider->ResultType());
 
-  if (provider->ShouldBlockZeroState())
+  if (ash::IsZeroStateResultType(provider->ResultType()))
     ++returned_zero_state_blockers_;
 
   if (!on_zero_state_done_) {
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.h b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
index 21f964dec..a5ce38c 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.h
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
@@ -62,6 +62,7 @@
   void OpenResult(ChromeSearchResult* result, int event_flags) override;
   void InvokeResultAction(ChromeSearchResult* result,
                           ash::SearchResultActionType action) override;
+  AppSearchDataSource* GetAppSearchDataSource() override;
   size_t AddGroup(size_t max_results) override;
   void AddProvider(size_t group_id,
                    std::unique_ptr<SearchProvider> provider) override;
@@ -145,6 +146,7 @@
   ResultsChangedCallback results_changed_callback_;
 
   std::unique_ptr<SearchMetricsManager> metrics_manager_;
+  std::unique_ptr<AppSearchDataSource> app_search_data_source_;
   using Providers = std::vector<std::unique_ptr<SearchProvider>>;
   Providers providers_;
   AppListModelUpdater* const model_updater_;
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc
index d1d7cad7..2b1db0fe 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc
@@ -40,11 +40,8 @@
 class TestSearchProvider : public SearchProvider {
  public:
   TestSearchProvider(ash::AppListSearchResultType result_type,
-                     bool block_zero_state,
                      base::TimeDelta delay)
-      : result_type_(result_type),
-        block_zero_state_(block_zero_state),
-        delay_(delay) {}
+      : result_type_(result_type), delay_(delay) {}
 
   ~TestSearchProvider() override = default;
 
@@ -53,8 +50,6 @@
     results_ = std::move(results);
   }
 
-  bool ShouldBlockZeroState() const override { return block_zero_state_; }
-
   ash::AppListSearchResultType ResultType() const override {
     return result_type_;
   }
@@ -78,7 +73,6 @@
 
   std::vector<std::unique_ptr<ChromeSearchResult>> results_;
   ash::AppListSearchResultType result_type_;
-  bool block_zero_state_;
   base::TimeDelta delay_;
 };
 
@@ -142,8 +136,8 @@
 // SimpleProvider.
 static std::unique_ptr<SearchProvider> kProvider;
 SearchProvider* SimpleProvider(ash::AppListSearchResultType result_type) {
-  kProvider = std::make_unique<TestSearchProvider>(result_type, false,
-                                                   base::Seconds(0));
+  kProvider =
+      std::make_unique<TestSearchProvider>(result_type, base::Seconds(0));
   return kProvider.get();
 }
 
@@ -592,7 +586,7 @@
   ranker_delegate_->SetCategoryRanks({{Category::kApps, 0.1}});
 
   auto provider = std::make_unique<TestSearchProvider>(Result::kInstalledApp,
-                                                       false, base::Seconds(1));
+                                                       base::Seconds(1));
   auto* provider_ptr = provider.get();
   search_controller_->AddProvider(0, std::move(provider));
 
@@ -626,16 +620,16 @@
 TEST_F(SearchControllerImplNewTest, ZeroStateResultsAreBlocked) {
   ranker_delegate_->SetCategoryRanks({{Category::kApps, 0.1}});
 
-  // Set up four providers, two are zero-state blocking. One is slow. The
-  // particular result types and categories don't matter.
-  auto provider_a = std::make_unique<TestSearchProvider>(
-      Result::kInstalledApp, true, base::Seconds(1));
-  auto provider_b = std::make_unique<TestSearchProvider>(
-      Result::kZeroStateFile, true, base::Seconds(2));
-  auto provider_c = std::make_unique<TestSearchProvider>(
-      Result::kOsSettings, false, base::Seconds(1));
-  auto provider_d = std::make_unique<TestSearchProvider>(
-      Result::kOmnibox, false, base::Seconds(4));
+  // Set up four providers, two provide zero-state results. One is slow. The
+  // particular result categories don't matter.
+  auto provider_a = std::make_unique<TestSearchProvider>(Result::kZeroStateApp,
+                                                         base::Seconds(1));
+  auto provider_b = std::make_unique<TestSearchProvider>(Result::kZeroStateFile,
+                                                         base::Seconds(2));
+  auto provider_c = std::make_unique<TestSearchProvider>(Result::kOsSettings,
+                                                         base::Seconds(1));
+  auto provider_d =
+      std::make_unique<TestSearchProvider>(Result::kOmnibox, base::Seconds(4));
 
   provider_a->SetNextResults(
       MakeResults({"a"}, {Category::kApps}, {-1}, {0.3}));
@@ -675,10 +669,10 @@
 TEST_F(SearchControllerImplNewTest, ZeroStateResultsGetTimedOut) {
   ranker_delegate_->SetCategoryRanks({{Category::kApps, 0.1}});
 
-  auto provider_a = std::make_unique<TestSearchProvider>(
-      Result::kInstalledApp, true, base::Seconds(1));
-  auto provider_b = std::make_unique<TestSearchProvider>(
-      Result::kZeroStateFile, true, base::Seconds(3));
+  auto provider_a = std::make_unique<TestSearchProvider>(Result::kZeroStateApp,
+                                                         base::Seconds(1));
+  auto provider_b = std::make_unique<TestSearchProvider>(Result::kZeroStateFile,
+                                                         base::Seconds(3));
 
   provider_a->SetNextResults(
       MakeResults({"a"}, {Category::kApps}, {-1}, {0.3}));
@@ -711,9 +705,9 @@
       std::make_unique<RankerDelegate>(&profile_, search_controller_.get()));
 
   auto drive_provider = std::make_unique<TestSearchProvider>(
-      Result::kZeroStateDrive, true, base::Seconds(0));
+      Result::kZeroStateDrive, base::Seconds(0));
   auto local_provider = std::make_unique<TestSearchProvider>(
-      Result::kZeroStateFile, true, base::Seconds(0));
+      Result::kZeroStateFile, base::Seconds(0));
 
   drive_provider->SetNextResults(MakeResults(
       {"drive_a", "drive_b"}, {Category::kUnknown, Category::kUnknown},
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc b/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc
index 15151b2..8f1413c 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
+#include "chrome/test/base/testing_profile.h"
 
 namespace app_list::test {
 
@@ -36,14 +37,15 @@
   void SetUp() override {
     ChromeAshTestBase::SetUp();
     search_controller_ = std::make_unique<SearchControllerImpl>(
-        /*model_updater=*/nullptr, &list_controller_, /*profile=*/nullptr,
-        /*notifier=*/nullptr);
+        /*model_updater=*/nullptr, &list_controller_,
+        /*notifier=*/nullptr, &profile_);
   }
   SearchController& search_controller() { return *search_controller_; }
   TestAppListControllerDelegate& list_controller() { return list_controller_; }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  TestingProfile profile_;
   TestAppListControllerDelegate list_controller_;
   std::unique_ptr<SearchControllerImpl> search_controller_;
 };
diff --git a/chrome/browser/ui/app_list/search/search_provider.cc b/chrome/browser/ui/app_list/search/search_provider.cc
index e8d2b7fa..caeffb60 100644
--- a/chrome/browser/ui/app_list/search/search_provider.cc
+++ b/chrome/browser/ui/app_list/search/search_provider.cc
@@ -40,8 +40,4 @@
   result_changed_callback_.Run();
 }
 
-bool SearchProvider::ShouldBlockZeroState() const {
-  return false;
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_provider.h b/chrome/browser/ui/app_list/search/search_provider.h
index 49ec4eb..d80d01b0 100644
--- a/chrome/browser/ui/app_list/search/search_provider.h
+++ b/chrome/browser/ui/app_list/search/search_provider.h
@@ -45,11 +45,6 @@
   // Returns the main result type created by this provider.
   virtual ash::AppListSearchResultType ResultType() const = 0;
 
-  // Returns true if this provider should prevent zero-state results from being
-  // published until it has returned. If this is true, a provider should only
-  // return results once per call to StartZeroState.
-  virtual bool ShouldBlockZeroState() const;
-
   void set_controller(SearchController* controller) {
     search_controller_ = controller;
   }
diff --git a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc
index bef79be..cb0f7fb 100644
--- a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.cc
@@ -67,8 +67,4 @@
   return ResultType::kUnknown;
 }
 
-bool TestContinueFilesSearchProvider::ShouldBlockZeroState() const {
-  return true;
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h
index 0187ca9..9a7b612 100644
--- a/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h
+++ b/chrome/browser/ui/app_list/search/test/test_continue_files_search_provider.h
@@ -26,7 +26,6 @@
   // SearchProvider overrides:
   void StartZeroState() override;
   ash::AppListSearchResultType ResultType() const override;
-  bool ShouldBlockZeroState() const override;
 
   void set_count(size_t count) { count_ = count; }
 
diff --git a/chrome/browser/ui/app_list/search/test/test_search_controller.cc b/chrome/browser/ui/app_list/search/test/test_search_controller.cc
index 295f892..daaac6e6 100644
--- a/chrome/browser/ui/app_list/search/test/test_search_controller.cc
+++ b/chrome/browser/ui/app_list/search/test/test_search_controller.cc
@@ -17,9 +17,8 @@
   // The search controller used when categorical search is enabled clears all
   // results when starging another search query - simulate this behavior in
   // tests when categorical search is enabled.
-  if (!ash::IsContinueSectionResultType(provider_->ResultType())) {
+  if (!ash::IsZeroStateResultType(provider_->ResultType()))
     last_results_.clear();
-  }
   provider_->Start(query);
 }
 
@@ -38,6 +37,10 @@
     ChromeSearchResult* result,
     ash::SearchResultActionType action) {}
 
+AppSearchDataSource* TestSearchController::GetAppSearchDataSource() {
+  return nullptr;
+}
+
 size_t TestSearchController::AddGroup(size_t max_results) {
   return 0u;
 }
diff --git a/chrome/browser/ui/app_list/search/test/test_search_controller.h b/chrome/browser/ui/app_list/search/test/test_search_controller.h
index 7214634..fd6c4da 100644
--- a/chrome/browser/ui/app_list/search/test/test_search_controller.h
+++ b/chrome/browser/ui/app_list/search/test/test_search_controller.h
@@ -12,6 +12,7 @@
 
 namespace app_list {
 
+class AppSearchDataSource;
 class SearchProvider;
 
 class TestSearchController : public SearchController {
@@ -32,6 +33,7 @@
   void OpenResult(ChromeSearchResult* result, int event_flags) override;
   void InvokeResultAction(ChromeSearchResult* result,
                           ash::SearchResultActionType action) override;
+  AppSearchDataSource* GetAppSearchDataSource() override;
   size_t AddGroup(size_t max_results) override;
   void AddProvider(size_t group_id,
                    std::unique_ptr<SearchProvider> provider) override;
diff --git a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
index 61b37844..2a61470 100644
--- a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
+++ b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
@@ -33,17 +33,11 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_MAC)
-#include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "base/mac/mac_util.h"
 #endif
 
 namespace {
 
-#if BUILDFLAG(IS_MAC)
-static constexpr char kBluetoothSettingsUri[] =
-    "x-apple.systempreferences:com.apple.preference.security?Privacy_"
-    "Bluetooth";
-#endif
-
 Browser* GetBrowser() {
   chrome::ScopedTabbedBrowserDisplayer browser_displayer(
       ProfileManager::GetLastUsedProfileAllowedByPolicy());
@@ -62,10 +56,7 @@
           CreateExtensionAwareChooserTitle(
               owner,
               IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN,
-              IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME)) {
-  web_contents_ =
-      content::WebContents::FromRenderFrameHost(owner)->GetWeakPtr();
-}
+              IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME)) {}
 
 ChromeBluetoothChooserController::~ChromeBluetoothChooserController() = default;
 
@@ -86,11 +77,8 @@
 
 void ChromeBluetoothChooserController::OpenPermissionPreferences() const {
 #if BUILDFLAG(IS_MAC)
-  if (web_contents_) {
-    ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
-        GURL(kBluetoothSettingsUri), web_contents_.get(),
-        content::WeakDocumentPtr());
-  }
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kPrivacySecurity_Bluetooth);
 #else
   NOTREACHED();
 #endif
diff --git a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.h b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.h
index abb7657..8910a1e 100644
--- a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.h
+++ b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.h
@@ -5,13 +5,11 @@
 #ifndef CHROME_BROWSER_UI_BLUETOOTH_CHROME_BLUETOOTH_CHOOSER_CONTROLLER_H_
 #define CHROME_BROWSER_UI_BLUETOOTH_CHROME_BLUETOOTH_CHOOSER_CONTROLLER_H_
 
-#include "base/memory/weak_ptr.h"
 #include "components/permissions/bluetooth_chooser_controller.h"
 #include "content/public/browser/bluetooth_chooser.h"
 
 namespace content {
 class RenderFrameHost;
-class WebContents;
 }  // namespace content
 
 // The concrete version of BluetoothChooserController for Chrome.
@@ -37,9 +35,6 @@
 
   // Opens the help center URL.
   void OpenHelpCenterUrl() const override;
-
- private:
-  base::WeakPtr<content::WebContents> web_contents_;
 };
 
 #endif  // CHROME_BROWSER_UI_BLUETOOTH_CHROME_BLUETOOTH_CHOOSER_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/share_menu_controller.mm b/chrome/browser/ui/cocoa/share_menu_controller.mm
index 3ae1cddc..3d7c42c 100644
--- a/chrome/browser/ui/cocoa/share_menu_controller.mm
+++ b/chrome/browser/ui/cocoa/share_menu_controller.mm
@@ -35,15 +35,6 @@
 
 namespace {
 
-NSString* const kExtensionPrefPanePath =
-    @"/System/Library/PreferencePanes/Extensions.prefPane";
-// Undocumented, used by Safari.
-const UInt32 kOpenSharingSubpaneDescriptorType = 'ptru';
-NSString* const kOpenSharingSubpaneActionKey = @"action";
-NSString* const kOpenSharingSubpaneActionValue = @"revealExtensionPoint";
-NSString* const kOpenSharingSubpaneProtocolKey = @"protocol";
-NSString* const kOpenSharingSubpaneProtocolValue = @"com.apple.share-services";
-
 // The reminder service doesn't have a convenient NSSharingServiceName*
 // constant.
 NSString* const kRemindersSharingServiceName =
@@ -225,26 +216,8 @@
 
 // Opens the "Sharing" subpane of the "Extensions" macOS preference pane.
 - (void)openSharingPrefs:(NSMenuItem*)sender {
-  NSURL* prefPaneURL =
-      [NSURL fileURLWithPath:kExtensionPrefPanePath isDirectory:YES];
-  NSDictionary* args = @{
-    kOpenSharingSubpaneActionKey : kOpenSharingSubpaneActionValue,
-    kOpenSharingSubpaneProtocolKey : kOpenSharingSubpaneProtocolValue
-  };
-  NSData* data = [NSPropertyListSerialization
-      dataWithPropertyList:args
-                    format:NSPropertyListXMLFormat_v1_0
-                   options:0
-                     error:nil];
-  base::scoped_nsobject<NSAppleEventDescriptor> descriptor(
-      [[NSAppleEventDescriptor alloc]
-          initWithDescriptorType:kOpenSharingSubpaneDescriptorType
-                            data:data]);
-  [[NSWorkspace sharedWorkspace] openURLs:@[ prefPaneURL ]
-                  withAppBundleIdentifier:nil
-                                  options:NSWorkspaceLaunchAsync
-           additionalEventParamDescriptor:descriptor
-                        launchIdentifiers:NULL];
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kPrivacySecurity_Extensions_Sharing);
 }
 
 // Returns the image to be used for the "More..." menu item, or nil on macOS
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index f0a131bc..2291934 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -111,6 +111,7 @@
   E_CPONLY(kColorInfoBarButtonIconDisabled) \
   E_CPONLY(kColorInfoBarContentAreaSeparator) \
   E_CPONLY(kColorInfoBarForeground) \
+  /* There is also a kColorInfoBarIcon in /ui/color/color_id.h */ \
   /* Intent Picker colors. */ \
   E_CPONLY(kColorIntentPickerItemBackgroundHovered) \
   E_CPONLY(kColorIntentPickerItemBackgroundSelected) \
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc
index 90bb2d9..bec439e 100644
--- a/chrome/browser/ui/color/chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -284,6 +284,12 @@
   mixer[kColorInfoBarContentAreaSeparator] =
       ui::AlphaBlend(kColorToolbarButtonIcon, kColorInfoBarBackground, 0x3A);
   mixer[kColorInfoBarForeground] = {kColorToolbarText};
+  // kColorInfoBarIcon is referenced in //components/infobars, so
+  // we can't use a color id from the chrome namespace. Here we're
+  // overriding the default color with something more suitable.
+  mixer[ui::kColorInfoBarIcon] =
+      ui::PickGoogleColor(ui::kColorAccent, kColorInfoBarBackground,
+                          color_utils::kMinimumVisibleContrastRatio);
   mixer[kColorIntentPickerItemBackgroundHovered] = ui::SetAlpha(
       ui::GetColorWithMaxContrast(ui::kColorDialogBackground), 0x0F);  // 6%.
   mixer[kColorIntentPickerItemBackgroundSelected] = ui::BlendForMinContrast(
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 41c9e1b..12a898f 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
 #include "chrome/browser/download/download_request_limiter.h"
-#include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/permission_bubble_media_access_handler.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
@@ -86,6 +85,7 @@
 #include "ui/resources/grit/ui_resources.h"
 
 #if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 #include "services/device/public/cpp/geolocation/geolocation_manager.h"
@@ -105,18 +105,6 @@
 
 using QuietUiReason = permissions::PermissionRequestManager::QuietUiReason;
 
-#if BUILDFLAG(IS_MAC)
-static constexpr char kCameraSettingsURI[] =
-    "x-apple.systempreferences:com.apple.preference.security?Privacy_"
-    "Camera";
-static constexpr char kMicSettingsURI[] =
-    "x-apple.systempreferences:com.apple.preference.security?Privacy_"
-    "Microphone";
-static constexpr char kLocationSettingsURI[] =
-    "x-apple.systempreferences:com.apple.preference.security?Privacy_"
-    "LocationServices";
-#endif  // BUILDFLAG(IS_MAC)
-
 // Returns a boolean indicating whether the setting should be managed by the
 // user (i.e. it is not controlled by policy). Also takes a (nullable) out-param
 // which is populated by the actual setting for the given URL.
@@ -905,11 +893,11 @@
     DCHECK(ShouldShowSystemMediaPermissions());
 
     if (CameraAccessed()) {
-      ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
-          GURL(kCameraSettingsURI), web_contents(), content::WeakDocumentPtr());
+      base::mac::OpenSystemSettingsPane(
+          base::mac::SystemSettingsPane::kPrivacySecurity_Camera);
     } else if (MicrophoneAccessed()) {
-      ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
-          GURL(kMicSettingsURI), web_contents(), content::WeakDocumentPtr());
+      base::mac::OpenSystemSettingsPane(
+          base::mac::SystemSettingsPane::kPrivacySecurity_Microphone);
     }
     return;
 #endif  // BUILDFLAG(IS_MAC)
@@ -1286,8 +1274,8 @@
           "ContentSettings.GeolocationDialog.OpenPreferencesClicked"));
     }
 
-    ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
-        GURL(kLocationSettingsURI), web_contents(), content::WeakDocumentPtr());
+    base::mac::OpenSystemSettingsPane(
+        base::mac::SystemSettingsPane::kPrivacySecurity_LocationServices);
     return;
 #endif  // BUILDFLAG(IS_MAC)
   }
diff --git a/chrome/browser/ui/views/exclusive_access_bubble_views.cc b/chrome/browser/ui/views/exclusive_access_bubble_views.cc
index 8a040ab..fc5557e3 100644
--- a/chrome/browser/ui/views/exclusive_access_bubble_views.cc
+++ b/chrome/browser/ui/views/exclusive_access_bubble_views.cc
@@ -221,13 +221,13 @@
     accelerator = browser_fullscreen_exit_accelerator_;
   } else {
     accelerator = l10n_util::GetStringUTF16(IDS_APP_ESC_KEY);
-  }
 #if BUILDFLAG(IS_MAC)
-  // Mac keyboards use lowercase for everything except function keys, which are
-  // typically reserved for system use. Since |accelerator| is placed in a box
-  // to make it look like a keyboard key it looks weird to not follow suit.
-  accelerator = base::i18n::ToLower(accelerator);
+    // Mac keyboards use lowercase for the non-letter keys, and since the key is
+    // placed in a box to make it look like a keyboard key it looks weird to not
+    // follow suit.
+    accelerator = base::i18n::ToLower(accelerator);
 #endif
+  }
   view_->UpdateContent(GetInstructionText(accelerator));
 }
 
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 015329b..284c21c 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -215,7 +215,11 @@
     "chrome://connection-help",
     "chrome://connection-monitoring-detected",
     "chrome://crashes",
+#if !((BUILDFLAG(IS_LINUX) && !defined(NDEBUG)) || defined(ADDRESS_SANITIZER))
+    // TODO(crbug.com/1380393): Failing on Linux debug builder and ASan (time
+    // out).
     "chrome://credits",
+#endif
     "chrome://device-log",
     "chrome://dino",
     // TODO(crbug.com/1113446): Test failure due to excessive output.
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
index e8da4947..5d7788f8 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
@@ -173,6 +173,7 @@
   local_printer_->GetCapability(
       device_name, base::BindOnce(CapabilityToValue).Then(std::move(callback)));
 }
+
 void LocalPrinterHandlerChromeos::StartPrint(
     const std::u16string& job_title,
     base::Value::Dict settings,
@@ -215,6 +216,48 @@
     settings.Set(kSettingUsername, *username);
     settings.Set(kSettingSendUserInfo, true);
   }
+
+  const std::string* const printer_id = settings.FindString(kSettingDeviceName);
+  crosapi::mojom::LocalPrinter::GetOAuthAccessTokenCallback cb =
+      base::BindOnce(&LocalPrinterHandlerChromeos::OnOAuthTokenReady,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(settings),
+                     std::move(print_data), std::move(callback));
+
+  if (!local_printer_) {
+    LOG(ERROR) << "Local printer not available";
+    std::move(cb).Run(crosapi::mojom::GetOAuthAccessTokenResult::NewError(
+        crosapi::mojom::OAuthError::New()));
+    return;
+  }
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (local_printer_version_ <
+      int{crosapi::mojom::LocalPrinter::MethodMinVersions::
+              kGetOAuthAccessTokenMinVersion}) {
+    LOG(WARNING) << "Ash LocalPrinter version " << local_printer_version_
+                 << " does not support GetOAuthToken().";
+    std::move(cb).Run(crosapi::mojom::GetOAuthAccessTokenResult::NewNone(
+        crosapi::mojom::OAuthNotNeeded::New()));
+    return;
+  }
+#endif
+
+  local_printer_->GetOAuthAccessToken(printer_id ? *printer_id : "",
+                                      std::move(cb));
+}
+
+void LocalPrinterHandlerChromeos::OnOAuthTokenReady(
+    base::Value::Dict settings,
+    scoped_refptr<base::RefCountedMemory> print_data,
+    PrinterHandler::PrintCallback callback,
+    crosapi::mojom::GetOAuthAccessTokenResultPtr oauth_result) {
+  if (oauth_result->is_token()) {
+    settings.Set(kSettingChromeOSAccessOAuthToken,
+                 oauth_result->get_token()->token);
+  } else if (oauth_result->is_error()) {
+    LOG(ERROR) << "Error when obtaining an oauth token for a local printer";
+  }
+
   StartLocalPrint(std::move(settings), std::move(print_data),
                   preview_web_contents_, std::move(callback));
 }
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
index b2b92284..14b173e 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
@@ -84,6 +84,11 @@
                               scoped_refptr<base::RefCountedMemory> print_data,
                               PrinterHandler::PrintCallback callback,
                               const absl::optional<std::string>& username);
+  void OnOAuthTokenReady(
+      base::Value::Dict settings,
+      scoped_refptr<base::RefCountedMemory> print_data,
+      PrinterHandler::PrintCallback callback,
+      crosapi::mojom::GetOAuthAccessTokenResultPtr oauth_result);
 
   const raw_ptr<content::WebContents> preview_web_contents_;
   raw_ptr<crosapi::mojom::LocalPrinter> local_printer_ = nullptr;
diff --git a/chrome/browser/ui/webui/sandbox/sandbox_handler.cc b/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
index 16658a7..3a455ba 100644
--- a/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
+++ b/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
@@ -89,8 +89,6 @@
       FeatureToValue(sandbox::policy::features::kSharedSandboxPolicies));
   features.Append(FeatureToValue(
       sandbox::policy::features::kWinSboxDisableExtensionPoints));
-  features.Append(
-      FeatureToValue(sandbox::policy::features::kWinSboxDisableKtmComponent));
   return features;
 }
 
diff --git a/chrome/browser/ui/webui/settings/settings_utils_mac.mm b/chrome/browser/ui/webui/settings/settings_utils_mac.mm
index e94a2dd0..9feca41 100644
--- a/chrome/browser/ui/webui/settings/settings_utils_mac.mm
+++ b/chrome/browser/ui/webui/settings/settings_utils_mac.mm
@@ -8,8 +8,7 @@
 
 #include "base/logging.h"
 #include "base/mac/mac_logging.h"
-#include "base/mac/scoped_aedesc.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/mac_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -35,23 +34,8 @@
 namespace settings_utils {
 
 void ShowNetworkProxySettings(content::WebContents* web_contents) {
-  NSArray* itemsToOpen =
-      @[ [NSURL fileURLWithPath:@"/System/Library/PreferencePanes/"
-                                @"Network.prefPane"] ];
-
-  const char* proxyPrefCommand = "Proxies";
-  base::mac::ScopedAEDesc<> openParams;
-  OSStatus status =
-      AECreateDesc('ptru', proxyPrefCommand, strlen(proxyPrefCommand),
-                   openParams.OutPointer());
-  OSSTATUS_LOG_IF(ERROR, status != noErr, status)
-      << "Failed to create open params";
-
-  LSLaunchURLSpec launchSpec = {0};
-  launchSpec.itemURLs = (CFArrayRef)itemsToOpen;
-  launchSpec.passThruParams = openParams;
-  launchSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
-  LSOpenFromURLSpec(&launchSpec, NULL);
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kNetwork_Proxies);
 }
 
 void ShowManageSSLCertificates(content::WebContents* web_contents) {
diff --git a/chrome/browser/vr/elements/scaled_depth_adjuster.cc b/chrome/browser/vr/elements/scaled_depth_adjuster.cc
index 63a315d..62587aa9 100644
--- a/chrome/browser/vr/elements/scaled_depth_adjuster.cc
+++ b/chrome/browser/vr/elements/scaled_depth_adjuster.cc
@@ -36,10 +36,7 @@
       inherited.PostConcat(anc->LocalTransform());
     }
   }
-  bool success = inherited.GetInverse(&transform_);
-  DCHECK(success);
-  if (!success)
-    return false;
+  transform_ = inherited.GetCheckedInverse();
   gfx::Point3F o = inherited.MapPoint(gfx::Point3F());
   float z = -o.z() + delta_z_;
   transform_.Scale3d(z, z, z);
diff --git a/chrome/browser/vr/elements/viewport_aware_root.cc b/chrome/browser/vr/elements/viewport_aware_root.cc
index afbbb38..e3ac459 100644
--- a/chrome/browser/vr/elements/viewport_aware_root.cc
+++ b/chrome/browser/vr/elements/viewport_aware_root.cc
@@ -46,9 +46,8 @@
   gfx::Vector3dF look_at = vr::GetForwardVector(head_pose);
   bool changed = AdjustRotationForHeadPose(look_at);
   if (recenter_on_rotate_) {
-    gfx::Transform world_from_head;
-    bool invertable = head_pose.GetInverse(&world_from_head);
-    DCHECK(invertable);  // Pose data has been validated already.
+    // Pose data has been validated already.
+    gfx::Transform world_from_head = head_pose.GetCheckedInverse();
     gfx::Point3F head_pos_in_world_space =
         world_from_head.MapPoint(gfx::Point3F());
     changed = AdjustTranslation(head_pos_in_world_space.x(),
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index 56f6a33..7f11e64 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -12,7 +12,6 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/observer_list.h"
-#include "base/process/launch.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -358,13 +357,8 @@
 #if BUILDFLAG(IS_MAC)
 void AuthenticatorRequestDialogModel::OpenBlePreferences() {
   DCHECK_EQ(current_step(), Step::kBlePermissionMac);
-
-  base::LaunchOptions opts;
-  opts.disclaim_responsibility = true;
-  base::LaunchProcess({"open",
-                       "x-apple.systempreferences:com.apple.preference."
-                       "security?Privacy_Bluetooth"},
-                      opts);
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kPrivacySecurity_Bluetooth);
 }
 #endif  // IS_MAC
 
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 3e602ac..ec66532 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1667282374-24c862692204ee2c5deef0d32c07b005d8520e56.profdata
+chrome-win32-main-1667314645-4ab448ff0ceee7e470574eb88603e0863606684e.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index e473bef..79a80f11 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1667217321-26e76a6404b888041e2dfe0b539b0d890b0820fc.profdata
+chrome-win64-main-1667314645-d086e5bdb062721f30f0176adb953e61d398384e.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1858b7d..d32d0c1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8553,6 +8553,7 @@
       "../browser/browser_switcher/mock_alternative_browser_driver.cc",
       "../browser/browser_switcher/mock_alternative_browser_driver.h",
       "../browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc",
+      "../browser/enterprise/idle/browser_closer_unittest.cc",
       "../browser/enterprise/remote_commands/rotate_attestation_credential_job_unittest.cc",
       "../browser/enterprise/signals/user_delegate_impl_unittest.cc",
       "../browser/net/disk_cache_dir_policy_handler_unittest.cc",
diff --git a/chrome/test/chromeos/printing/fake_local_printer_chromeos.cc b/chrome/test/chromeos/printing/fake_local_printer_chromeos.cc
index f038bca..80fc6bb 100644
--- a/chrome/test/chromeos/printing/fake_local_printer_chromeos.cc
+++ b/chrome/test/chromeos/printing/fake_local_printer_chromeos.cc
@@ -78,3 +78,9 @@
     AddPrintJobObserverCallback callback) {
   FAIL();
 }
+
+void FakeLocalPrinter::GetOAuthAccessToken(
+    const std::string& printer_id,
+    GetOAuthAccessTokenCallback callback) {
+  FAIL();
+}
diff --git a/chrome/test/chromeos/printing/fake_local_printer_chromeos.h b/chrome/test/chromeos/printing/fake_local_printer_chromeos.h
index ff989ace..ef4acedf 100644
--- a/chrome/test/chromeos/printing/fake_local_printer_chromeos.h
+++ b/chrome/test/chromeos/printing/fake_local_printer_chromeos.h
@@ -42,6 +42,8 @@
       mojo::PendingRemote<crosapi::mojom::PrintJobObserver> remote,
       crosapi::mojom::PrintJobSource source,
       AddPrintJobObserverCallback callback) override;
+  void GetOAuthAccessToken(const std::string& printer_id,
+                           GetOAuthAccessTokenCallback callback) override;
 };
 
 #endif  // CHROME_TEST_CHROMEOS_PRINTING_FAKE_LOCAL_PRINTER_CHROMEOS_H_
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts
index 81cbfc7fe..26ceb3b 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_grid_item_element_test.ts
@@ -266,4 +266,25 @@
         'none', getComputedStyle(querySelector('iron-icon')!).display,
         'iron-icon is display none when aria selected is false');
   });
+
+  test('sets aria-disabled attribute', async () => {
+    wallpaperGridItemElement = initElement(WallpaperGridItem);
+    await waitAfterNextRender(wallpaperGridItemElement);
+
+    assertEquals(
+        'false', wallpaperGridItemElement.getAttribute('aria-disabled'),
+        'aria-disabled defaults to false');
+
+    wallpaperGridItemElement.disabled = true;
+    await waitAfterNextRender(wallpaperGridItemElement);
+    assertEquals(
+        'true', wallpaperGridItemElement.getAttribute('aria-disabled'),
+        'disabled sets aria-disabled attribute');
+
+    wallpaperGridItemElement.disabled = false;
+    await waitAfterNextRender(wallpaperGridItemElement);
+    assertEquals(
+        'false', wallpaperGridItemElement.getAttribute('aria-disabled'),
+        'disabled false sets aria-disabled attribute false');
+  });
 });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
index 57bc393..4775dda 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
@@ -6,7 +6,7 @@
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {OnlineImageType, PersonalizationRouter, WallpaperGridItem, WallpaperImages} from 'chrome://personalization/js/personalization_app.js';
-import {assertDeepEquals, assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
@@ -51,14 +51,15 @@
 
   test('sets aria-selected for current wallpaper asset id', async () => {
     wallpaperImagesElement = await createWithDefaultData();
-    const selectedElement: WallpaperGridItem =
-        wallpaperImagesElement.shadowRoot!.querySelector(
-            `${WallpaperGridItem.is}[aria-selected='true']`)!;
+    const selectedElements: WallpaperGridItem[] =
+        Array.from(wallpaperImagesElement.shadowRoot!.querySelectorAll(
+            `${WallpaperGridItem.is}[aria-selected='true']`));
 
-    assertEquals(
-        selectedElement!.dataset['assetId'],
-        personalizationStore.data.wallpaper.currentSelected.key,
-        'aria selected element has correct asset id');
+    assertEquals(selectedElements.length, 1, '1 item aria selected');
+    assertDeepEquals(
+        selectedElements[0]!.src,
+        [wallpaperProvider.images![2]!.url, wallpaperProvider.images![0]!.url],
+        `item has correct src`);
 
     const notSelectedElements: HTMLDivElement[] =
         Array.from(wallpaperImagesElement.shadowRoot!.querySelectorAll(
@@ -70,17 +71,6 @@
     assertEquals(
         uniqueUnitIds.size - 1, notSelectedElements.length,
         'correct number of non-selected elements');
-
-    for (const notSelected of notSelectedElements) {
-      assertTrue(
-          notSelected.dataset['assetId']!.length > 0,
-          'not selected elements have an assetId');
-
-      assertNotEquals(
-          wallpaperProvider.currentWallpaper.key,
-          notSelected.dataset['assetId'],
-          'not selected elements have a different assetId');
-    }
   });
 
   test('displays images for current collectionId', async () => {
@@ -127,25 +117,25 @@
     await waitAfterNextRender(wallpaperImagesElement);
 
     assertDeepEquals(
-        ['1', '2'],
+        ['Image 0-1', 'Image 0-2'],
         Array
             .from(wallpaperImagesElement.shadowRoot!
                       .querySelectorAll<WallpaperGridItem>(
                           `${WallpaperGridItem.is}:not([hidden])`))
-            .map(elem => elem.dataset['assetId']),
-        'expected asset ids are displayed for collectionId `id_0`');
+            .map(elem => elem.getAttribute('aria-label')),
+        'expected aria labels are displayed for collectionId `id_0`');
 
     wallpaperImagesElement.collectionId = 'id_1';
     await waitAfterNextRender(wallpaperImagesElement);
 
     assertDeepEquals(
-        ['10', '20'],
+        ['Image 1-10', 'Image 1-20'],
         Array
             .from(wallpaperImagesElement.shadowRoot!
                       .querySelectorAll<WallpaperGridItem>(
                           `${WallpaperGridItem.is}:not([hidden])`))
-            .map(elem => elem.dataset['assetId']),
-        'expected asset ids are displayed for collectionId `id_1`');
+            .map(elem => elem.getAttribute('aria-label')),
+        'expected aria labels are displayed for collectionId `id_1`');
   });
 
   test('displays dark light tile for images with same unitId', async () => {
@@ -179,9 +169,10 @@
 
   test('selects an image when clicked', async () => {
     wallpaperImagesElement = await createWithDefaultData();
+    // Click the first image that is not currently selected.
     wallpaperImagesElement.shadowRoot!
         .querySelector<WallpaperGridItem>(
-            `${WallpaperGridItem.is}[data-asset-id='2']`)!.click();
+            `${WallpaperGridItem.is}[aria-selected='false']`)!.click();
     const [assetId, previewMode] =
         await wallpaperProvider.whenCalled('selectWallpaper');
     assertEquals(2n, assetId, 'correct asset id is passed');
diff --git a/chrome/test/data/webui/settings/chromeos/cursor_and_touchpad_page_tests.js b/chrome/test/data/webui/settings/chromeos/cursor_and_touchpad_page_tests.js
new file mode 100644
index 0000000..7809c8ac
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/cursor_and_touchpad_page_tests.js
@@ -0,0 +1,253 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://os-settings/chromeos/lazy_load.js';
+
+import {DevicePageBrowserProxy, DevicePageBrowserProxyImpl, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+
+suite('CursorAndTouchpadPageTests', function() {
+  let page = null;
+  let deviceBrowserProxy = null;
+
+  /** @implements {DevicePageBrowserProxy} */
+  class TestDevicePageBrowserProxy {
+    constructor() {
+      /** @private {boolean} */
+      this.hasMouse_ = true;
+      /** @private {boolean} */
+      this.hasTouchpad_ = true;
+    }
+
+    /** @param {boolean} hasMouse */
+    set hasMouse(hasMouse) {
+      this.hasMouse_ = hasMouse;
+      webUIListenerCallback('has-mouse-changed', this.hasMouse_);
+    }
+
+    /** @param {boolean} hasTouchpad */
+    set hasTouchpad(hasTouchpad) {
+      this.hasTouchpad_ = hasTouchpad;
+      webUIListenerCallback('has-touchpad-changed', this.hasTouchpad_);
+    }
+
+    /** @override */
+    initializePointers() {
+      webUIListenerCallback('has-mouse-changed', this.hasMouse_);
+      webUIListenerCallback('has-touchpad-changed', this.hasTouchpad_);
+    }
+  }
+
+  function initPage(opt_prefs) {
+    page = document.createElement('settings-cursor-and-touchpad-page');
+    page.prefs = opt_prefs || getDefaultPrefs();
+    document.body.appendChild(page);
+  }
+
+  function getDefaultPrefs() {
+    return {
+      'settings': {
+        'a11y': {
+          'tablet_mode_shelf_nav_buttons_enabled': {
+            key: 'settings.a11y.tablet_mode_shelf_nav_buttons_enabled',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+        },
+        'accessibility': {
+          key: 'settings.accessibility',
+          type: chrome.settingsPrivate.PrefType.BOOLEAN,
+          value: false,
+        },
+      },
+    };
+  }
+
+  setup(function() {
+    deviceBrowserProxy = new TestDevicePageBrowserProxy();
+    DevicePageBrowserProxyImpl.setInstanceForTesting(deviceBrowserProxy);
+
+    PolymerTest.clearBody();
+    Router.getInstance().navigateTo(routes.A11Y_CURSOR_AND_TOUCHPAD);
+  });
+
+  teardown(function() {
+    if (page) {
+      page.remove();
+    }
+    Router.getInstance().resetRouteForTesting();
+  });
+
+  test(
+      'should focus pointerSubpageButton button when returning from Pointers subpage',
+      async () => {
+        const selector = '#pointerSubpageButton';
+        const route = routes.POINTERS;
+        initPage();
+        flush();
+        const router = Router.getInstance();
+
+        const subpageButton = page.shadowRoot.querySelector(selector);
+        assertTrue(!!subpageButton);
+
+        subpageButton.click();
+        assertEquals(route, router.getCurrentRoute());
+        assertNotEquals(
+            subpageButton, page.shadowRoot.activeElement,
+            `${selector} should not be focused`);
+
+        const popStateEventPromise = eventToPromise('popstate', window);
+        router.navigateToPreviousRoute();
+        await popStateEventPromise;
+        await waitBeforeNextRender(page);
+
+        assertEquals(routes.A11Y_CURSOR_AND_TOUCHPAD, router.getCurrentRoute());
+        assertEquals(
+            subpageButton, page.shadowRoot.activeElement,
+            `${selector} should be focused`);
+      });
+
+  test('Pointers row only visible if mouse/touchpad present', function() {
+    initPage();
+    const row = page.shadowRoot.querySelector('#pointerSubpageButton');
+    assertFalse(row.hidden);
+
+    // Has touchpad, doesn't have mouse ==> not hidden.
+    deviceBrowserProxy.hasMouse = false;
+    assertFalse(row.hidden);
+
+    // Doesn't have either ==> hidden.
+    deviceBrowserProxy.hasTouchpad = false;
+    assertTrue(row.hidden);
+
+    // Has mouse, doesn't have touchpad ==> not hidden.
+    deviceBrowserProxy.hasMouse = true;
+    assertFalse(row.hidden);
+
+    // Has both ==> not hidden.
+    deviceBrowserProxy.hasTouchpad = true;
+    assertFalse(row.hidden);
+  });
+
+  test('tablet mode buttons visible', function() {
+    loadTimeData.overrideValues({
+      isKioskModeActive: false,
+      showTabletModeShelfNavigationButtonsSettings: true,
+    });
+    initPage();
+    flush();
+
+    assertTrue(isVisible(page.shadowRoot.querySelector(
+        '#shelfNavigationButtonsEnabledControl')));
+  });
+
+  test('toggle tablet mode buttons', function() {
+    loadTimeData.overrideValues({
+      isKioskModeActive: false,
+      showTabletModeShelfNavigationButtonsSettings: true,
+    });
+    initPage();
+    flush();
+
+    const navButtonsToggle =
+        page.shadowRoot.querySelector('#shelfNavigationButtonsEnabledControl');
+    assertTrue(isVisible(navButtonsToggle));
+    // The default pref value is false.
+    assertFalse(navButtonsToggle.checked);
+
+    // Clicking the toggle should update the toggle checked value, and the
+    // backing preference.
+    navButtonsToggle.click();
+    flush();
+
+    assertTrue(navButtonsToggle.checked);
+    assertFalse(navButtonsToggle.disabled);
+    assertTrue(
+        page.prefs.settings.a11y.tablet_mode_shelf_nav_buttons_enabled.value);
+
+    navButtonsToggle.click();
+    flush();
+
+    assertFalse(navButtonsToggle.checked);
+    assertFalse(navButtonsToggle.disabled);
+    assertFalse(
+        page.prefs.settings.a11y.tablet_mode_shelf_nav_buttons_enabled.value);
+  });
+
+  test('tablet mode buttons toggle disabled with spoken feedback', function() {
+    loadTimeData.overrideValues({
+      isKioskModeActive: false,
+      showTabletModeShelfNavigationButtonsSettings: true,
+    });
+
+    const prefs = getDefaultPrefs();
+    // Enable spoken feedback.
+    prefs.settings.accessibility.value = true;
+
+    initPage(prefs);
+    flush();
+
+    const navButtonsToggle =
+        page.shadowRoot.querySelector('#shelfNavigationButtonsEnabledControl');
+    assertTrue(isVisible(navButtonsToggle));
+
+    // If spoken feedback is enabled, the shelf nav buttons toggle should be
+    // disabled and checked.
+    assertTrue(navButtonsToggle.disabled);
+    assertTrue(navButtonsToggle.checked);
+
+    // Clicking the toggle should have no effect.
+    navButtonsToggle.click();
+    flush();
+
+    assertTrue(navButtonsToggle.disabled);
+    assertTrue(navButtonsToggle.checked);
+    assertFalse(
+        page.prefs.settings.a11y.tablet_mode_shelf_nav_buttons_enabled.value);
+
+    // The toggle should be enabled if the spoken feedback gets disabled.
+    page.set('prefs.settings.accessibility.value', false);
+    flush();
+
+    assertFalse(!!navButtonsToggle.disabled);
+    assertFalse(navButtonsToggle.checked);
+    assertFalse(
+        page.prefs.settings.a11y.tablet_mode_shelf_nav_buttons_enabled.value);
+
+    // Clicking the toggle should update the backing pref.
+    navButtonsToggle.click();
+    flush();
+
+    assertFalse(!!navButtonsToggle.disabled);
+    assertTrue(navButtonsToggle.checked);
+    assertTrue(
+        page.prefs.settings.a11y.tablet_mode_shelf_nav_buttons_enabled.value);
+  });
+
+  test('some parts are hidden in kiosk mode', function() {
+    loadTimeData.overrideValues({
+      isKioskModeActive: true,
+      showTabletModeShelfNavigationButtonsSettings: true,
+    });
+    initPage();
+    // Add mouse and touchpad to show some hidden settings.
+    deviceBrowserProxy.hasMouse = true;
+    deviceBrowserProxy.hasTouchpad = true;
+    flush();
+
+    // Shelf navigation buttons are not shown in kiosk mode, even if
+    // showTabletModeShelfNavigationButtonsSettings is true.
+    assertFalse(isVisible(page.shadowRoot.querySelector(
+        '#shelfNavigationButtonsEnabledControl')));
+
+    const subpageLinks = page.root.querySelectorAll('cr-link-row');
+    subpageLinks.forEach(subpageLink => assertFalse(isVisible(subpageLink)));
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/display_and_magnification_page_tests.js b/chrome/test/data/webui/settings/chromeos/display_and_magnification_page_tests.js
new file mode 100644
index 0000000..5170a31c0
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/display_and_magnification_page_tests.js
@@ -0,0 +1,79 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://os-settings/chromeos/lazy_load.js';
+
+import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+
+suite('DisplayAndMagnificationPageTests', function() {
+  let page = null;
+
+  function initPage() {
+    page = document.createElement('settings-display-and-magnification-page');
+    document.body.appendChild(page);
+  }
+
+  setup(function() {
+    PolymerTest.clearBody();
+    loadTimeData.overrideValues(
+        {isAccessibilityOSSettingsVisibilityEnabled: true});
+    Router.getInstance().navigateTo(routes.A11Y_DISPLAY_AND_MAGNIFICATION);
+  });
+
+  teardown(function() {
+    if (page) {
+      page.remove();
+    }
+    Router.getInstance().resetRouteForTesting();
+  });
+
+  [{selector: '#displaySubpageButton', route: routes.DISPLAY},
+  ].forEach(({selector, route}) => {
+    test(
+        `should focus ${selector} button when returning from ${
+            route.path} subpage`,
+        async () => {
+          initPage();
+          flush();
+          const router = Router.getInstance();
+
+          const subpageButton = page.shadowRoot.querySelector(selector);
+          assertTrue(!!subpageButton);
+
+          subpageButton.click();
+          assertEquals(route, router.getCurrentRoute());
+          assertNotEquals(
+              subpageButton, page.shadowRoot.activeElement,
+              `${selector} should not be focused`);
+
+          const popStateEventPromise = eventToPromise('popstate', window);
+          router.navigateToPreviousRoute();
+          await popStateEventPromise;
+          await waitBeforeNextRender(page);
+
+          assertEquals(
+              routes.A11Y_DISPLAY_AND_MAGNIFICATION, router.getCurrentRoute());
+          assertEquals(
+              subpageButton, page.shadowRoot.activeElement,
+              `${selector} should be focused`);
+        });
+  });
+
+  test('no subpages are available in kiosk mode', function() {
+    loadTimeData.overrideValues({
+      isKioskModeActive: true,
+      showTabletModeShelfNavigationButtonsSettings: true,
+    });
+    initPage();
+    flush();
+
+    const subpageLinks = page.root.querySelectorAll('cr-link-row');
+    subpageLinks.forEach(subpageLink => assertFalse(isVisible(subpageLink)));
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index b79d3dd..832d09f 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -348,7 +348,17 @@
  ['CupsPrinterEntry', 'cups_printer_entry_tests.js'],
  ['CupsPrinterLandingPage', 'cups_printer_landing_page_tests.js'],
  ['CupsPrinterPage', 'cups_printer_page_tests.js'],
+ [
+   'CursorAndTouchpadPage',
+   'cursor_and_touchpad_page_tests.js',
+   {enabled: ['features::kAccessibilityOSSettingsVisibility']},
+ ],
  ['DateTimePage', 'date_time_page_tests.js'],
+ [
+   'DisplayAndMagnificationPage',
+   'display_and_magnification_page_tests.js',
+   {enabled: ['features::kAccessibilityOSSettingsVisibility']},
+ ],
  ['EsimInstallErrorDialog', 'esim_install_error_dialog_test.js'],
  ['EsimRemoveProfileDialog', 'esim_remove_profile_dialog_test.js'],
  ['EsimRenameDialog', 'esim_rename_dialog_test.js'],
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 36dfddf..c9f90b7 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -822,7 +822,12 @@
     }
 
     if (is_linux) {
-      sources += [ "test/integration_tests_linux.cc" ]
+      sources += [
+        "app/server/linux/rpc_unittests.cc",
+        "test/integration_tests_linux.cc",
+      ]
+
+      deps += [ "//chrome/updater/app/server/linux/mojom" ]
     }
   }
 
diff --git a/chrome/updater/app/server/linux/mojom/updater_service.mojom b/chrome/updater/app/server/linux/mojom/updater_service.mojom
index 83b6500..f278338 100644
--- a/chrome/updater/app/server/linux/mojom/updater_service.mojom
+++ b/chrome/updater/app/server/linux/mojom/updater_service.mojom
@@ -4,10 +4,295 @@
 
 module updater.mojom;
 
+import "mojo/public/mojom/base/file_path.mojom";
+
+// Struct containing the information required to register an application with
+// the updater. Passed from client to server during RegisterApp and Install.
+struct RegistrationRequest {
+  // Application ID of the app.
+  string app_id;
+
+  // The brand code, a four character code attributing the app’s
+  // presence to a marketing campaign or similar effort. May be the empty
+  // string.
+  string brand_code;
+
+  // A file path. Currently applicable to on Mac only: if a valid plist file
+  // exists at this path, the string value of key "KSBrandID" will override
+  // the `brand_code` above.
+  mojo_base.mojom.FilePath brand_path;
+
+  // The ap value (e.g. from a tagged metainstaller). May be the empty string.
+  // This typically indicates channel, though it can carry additional data as
+  // well.
+  string ap;
+
+  // The version of the app already installed. 0.0.0.0 if the app is not
+  // already installed.
+  string version;
+
+  // A file path. A file exists at this path if and only if the app is
+  // still installed. This is used (on Mac, for example) to detect
+  // whether an app has been uninstalled via deletion. May be the empty
+  // string; if so, the app is assumed to be installed unconditionally.
+  mojo_base.mojom.FilePath existence_checker_path;
+};
+
+// Struct containing details on the state of an update. It is passed
+// periodically from the server to the client via the StateChangeObserver.
+struct UpdateState {
+  // Possible states for updating an app. Add new values at the end of
+  // the definition, and do not mutate the existing values.
+  enum State {
+    // This value represents the absence of a state. No update request has
+    // yet been issued.
+    kUnknown = 0,
+
+    // This update has not been started, but has been requested.
+    kNotStarted,
+
+    // The engine began issuing an update check request.
+    kCheckingForUpdates,
+
+    // An update is available.
+    kUpdateAvailable,
+
+    // The engine began downloading an update.
+    kDownloading,
+
+    // The engine began running installation scripts.
+    kInstalling,
+
+    // The engine found and installed an update for this product. The update
+    // is complete and the state will not change.
+    kUpdated,
+
+    // The engine checked for updates. This product is already up to date.
+    // No update has been installed for this product. The update is complete
+    // and the state will not change.
+    kNoUpdate,
+
+    // The engine encountered an error updating this product. The update has
+    // halted and the state will not change.
+    kUpdateError,
+  };
+
+  string app_id;
+  State state = State.kUnknown;
+
+  // The version is initialized only after an update check has completed, and
+  // an update is available.
+  string next_version;
+
+  int64 downloaded_bytes = -1;
+  int64 total_bytes = -1;
+
+  // A value in the range [0, 100] if the install progress is known, or -1
+  // if the install progress is not available or it could not be computed.
+  int8 install_progress = -1;
+
+  UpdateService.ErrorCategory error_category =
+    UpdateService.ErrorCategory.kNone;
+  int32 error_code = 0;
+  int32 extra_code1 = 0;
+
+  // Results collected from installer result API. See the definition of
+  // `update_client::CrxInstaller::Result` for the meaning of the members.
+  string installer_text;
+  string installer_cmd_line;
+};
+
+struct AppState {
+  string app_id;
+  string version;
+  string ap;
+  string brand_code;
+  mojo_base.mojom.FilePath brand_path;
+  mojo_base.mojom.FilePath ecp;
+};
+
 // The UpdateService is the cross-platform core of the updater.
 // All functions and callbacks must be called on the same sequence.
 interface UpdateService {
+  // Defines the behavior of the update stack for over-installs.
+  // Typically, same versions updates are not allowed, in which case, the update
+  // server replies with `update not available'. But there are cases, such as
+  // re-installing an application again, when the server may respond with an
+  // update.
+  enum PolicySameVersionUpdate {
+    // The embedder does not allow over-installs with the same version. In this
+    // case, the server is expected to return `update not available` when it
+    // is queried for updates.
+    kNotAllowed = 0,
+
+    // The embedder is capable of handling updates with the same version, and
+    // the server may respond with such an update.
+    kAllowed,
+  };
+
+  enum Result {
+    // Indicates that the service successfully handled the non-blocking function
+    // invocation. Returning this value provides no indication regarding the
+    // outcome of the function, such as whether the updates succeeded or not.
+    kSuccess = 0,
+
+    // The function failed because there is an update in progress. Certain
+    // service functions can be parallelized but not all functions can run
+    // concurrently.
+    kUpdateInProgress,
+
+    // Not used. TODO(crbug.com/1014591).
+    kUpdateCanceled,
+
+    // The function failed because of a throttling policy such as load shedding.
+    kRetryLater,
+
+    // This is a generic result indicating that an error occurred in the service
+    // such as a task failed to post, or allocation of a resource failed.
+    kServiceFailed,
+
+    // An error handling the update check occurred.
+    kUpdateCheckFailed,
+
+    // This value indicates that required metadata associated with the
+    // application was not available for any reason.
+    kAppNotFound,
+
+    // A function argument was invalid.
+    kInvalidArgument,
+
+    // This server is not the active server.
+    kInactive,
+
+    // IPC connection to the remote process failed for some reason.
+    kIPCConnectionFailed,
+
+    // Failed to run app installer.
+    kInstallFailed,
+  };
+
+  // Run time errors are organized in specific categories to indicate the
+  // component where such errors occurred. The category appears as a numeric
+  // value in the telemetry pings. The values of this enum must be kept stable.
+  enum ErrorCategory {
+    kNone = 0,
+    kDownload,
+    kUnpack,
+    kInstall,
+    kService,
+    kUpdateCheck,
+  };
+
+  // Urgency of the update service invocation.
+  enum Priority {
+    // The caller has not set a valid priority value.
+    kUnknown = 0,
+
+    // The user is not waiting for this update.
+    kBackground,
+
+    // The user is not waiting for this update.
+    kForeground,
+  };
+
   // Returns the version of the active updater. The version object is invalid
   // if an error (including timeout) occurs.
   GetVersion() => (string version);
+
+  // Fetches policies from device management.
+  FetchPolicies() => (int32 result);
+
+  // Registers given request to the updater.
+  RegisterApp(RegistrationRequest request) => (int32 result);
+
+  // Gets state of all registered apps.
+  GetAppStates() => (array<AppState> app_states);
+
+  // Runs periodic tasks such as checking for uninstallation of registered
+  // applications or doing background updates for registered applications.
+  RunPeriodicTasks() => ();
+
+  // Initiates an update check for all registered applications. Receives state
+  // change notifications through the `state_change_observer`. Responds once
+  // the operation is complete.
+  UpdateAll() =>
+    (pending_receiver<StateChangeObserver> observer);
+
+  // Updates specified product. This update may be on-demand.
+  //
+  // Response:
+  //    The final result from the update engine.
+  Update(
+    // ID of app to update.
+    string app_id,
+
+    // Index of the server install data.
+    string install_data_index,
+
+    // Priority for processing this update.
+    Priority priority,
+
+    //  Whether a same-version update is allowed.
+    PolicySameVersionUpdate policy_same_version_update) =>
+    (pending_receiver<StateChangeObserver> observer);
+
+  // Registers and installs an application from the network.
+  //
+  // Response:
+  //    The final result from the update engine.
+  Install(
+    // Registration data about the app.
+    RegistrationRequest registration,
+
+    // User provided install data.
+    string client_install_data,
+
+    // Index of the server install data. Effective only
+    // when `client_install_data` is not set.
+    string install_data_index,
+
+    // Priority for processing this update.
+    Priority priority) =>
+    (pending_receiver<StateChangeObserver> observer);
+
+  // Cancels any ongoing installations of the specified product. This does not
+  // interrupt any product installers that are currently running, but does
+  // prevent them from being run if they are not yet downloaded.
+  //
+  // Args:
+  //   `app_id`: ID of the product to cancel installs of.
+  CancelInstalls(string app_id);
+
+  // Install an app by running its installer.
+  //
+  // Result:
+  //    The final result from the update engine.
+  RunInstaller(
+    // `app_id`: ID of app to install.
+    string app_id,
+
+    // `app_installer`: Offline installer path.
+    mojo_base.mojom.FilePath installer_path,
+
+    // `arguments`: Arguments to run the installer.
+    string install_args,
+
+    // `install_data`: Server install data extracted from the offline manifest.
+    string install_data,
+
+    // `install_settings`: An optional serialized dictionary to customize the
+    // installation.
+    string install_settings) =>
+    (pending_receiver<StateChangeObserver> observer);
+};
+
+// Callback interface for repeated state change notifications produced by
+// methods of the UpdateService interface.
+interface StateChangeObserver {
+  // Repeated state change callback.
+  // state: the new state of this update request.
+  OnStateChange(UpdateState state);
+
+  // A callback to be run with the final result of the operation.
+  OnComplete(UpdateService.Result result);
 };
diff --git a/chrome/updater/app/server/linux/rpc_unittests.cc b/chrome/updater/app/server/linux/rpc_unittests.cc
new file mode 100644
index 0000000..bd7551a
--- /dev/null
+++ b/chrome/updater/app/server/linux/rpc_unittests.cc
@@ -0,0 +1,325 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/version.h"
+#include "chrome/updater/app/server/linux/mojom/updater_service.mojom.h"
+#include "chrome/updater/app/server/linux/update_service_stub.h"
+#include "chrome/updater/ipc/update_service_proxy_linux.h"
+#include "chrome/updater/registration_data.h"
+#include "chrome/updater/update_service.h"
+#include "chrome/updater/updater_scope.h"
+#include "mojo/core/embedder/embedder.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+class FakeUpdateService : public UpdateService {
+ public:
+  void GetVersion(
+      base::OnceCallback<void(const base::Version&)> callback) override {
+    std::move(callback).Run(base::Version("9"));
+  }
+
+  void FetchPolicies(base::OnceCallback<void(int)> callback) override {
+    std::move(callback).Run(42);
+  }
+
+  void RegisterApp(const RegistrationRequest& request,
+                   base::OnceCallback<void(int)> callback) override {
+    std::move(callback).Run(42);
+  }
+
+  void GetAppStates(base::OnceCallback<void(const std::vector<AppState>&)>
+                        callback) override {
+    AppState ex1;
+    ex1.app_id = "ex1";
+    ex1.version = base::Version("9.19.20");
+    ex1.ap = "foo";
+    ex1.brand_code = "FooBarInc";
+    ex1.brand_path = base::FilePath("/path/to/foo_bar");
+    ex1.ecp = base::FilePath("path/to/foo_ecp");
+
+    AppState ex2;
+    ex2.app_id = "ex2";
+    ex2.version = base::Version("98.4.5");
+    ex2.ap = "zaz";
+    ex2.brand_code = "BazInc";
+    ex2.brand_path = base::FilePath("/path/to/baz");
+    ex2.ecp = base::FilePath("path/to/baz_ecp");
+
+    std::move(callback).Run({ex1, ex2});
+  }
+
+  void RunPeriodicTasks(base::OnceClosure callback) override {
+    std::move(callback).Run();
+  }
+
+  void UpdateAll(StateChangeCallback state_update, Callback callback) override {
+    DoStateChangeCallbacks(std::move(state_update), std::move(callback));
+  }
+
+  void Update(const std::string& app_id,
+              const std::string& install_data_index,
+              Priority priority,
+              PolicySameVersionUpdate policy_same_version_update,
+              StateChangeCallback state_update,
+              Callback callback) override {
+    DoStateChangeCallbacks(std::move(state_update), std::move(callback));
+  }
+
+  void Install(const RegistrationRequest& registration,
+               const std::string& client_install_data,
+               const std::string& install_data_index,
+               Priority priority,
+               StateChangeCallback state_update,
+               Callback callback) override {
+    DoStateChangeCallbacks(std::move(state_update), std::move(callback));
+  }
+
+  void CancelInstalls(const std::string& app_id) override {}
+
+  void RunInstaller(const std::string& app_id,
+                    const base::FilePath& installer_path,
+                    const std::string& install_args,
+                    const std::string& install_data,
+                    const std::string& install_settings,
+                    StateChangeCallback state_update,
+                    Callback callback) override {
+    DoStateChangeCallbacks(std::move(state_update), std::move(callback));
+  }
+
+  void Uninitialize() override {}
+
+ private:
+  ~FakeUpdateService() override = default;
+
+  void DoStateChangeCallbacks(StateChangeCallback state_update,
+                              Callback callback) {
+    UpdateService::UpdateState state1;
+    state1.app_id = "ex1";
+    state1.state = UpdateService::UpdateState::State::kCheckingForUpdates;
+    state_update.Run(state1);
+
+    UpdateService::UpdateState state2;
+    state2.app_id = "ex2";
+    state2.state = UpdateService::UpdateState::State::kDownloading;
+    state2.next_version = base::Version("3.14");
+    state2.downloaded_bytes = 1024;
+    state2.total_bytes = 2048;
+    state_update.Run(state2);
+
+    UpdateService::UpdateState state3;
+    state3.app_id = "ex3";
+    state3.state = UpdateService::UpdateState::State::kUpdateError;
+    state3.install_progress = 99;
+    state3.error_code = 0xDEAD;
+    state3.extra_code1 = 0xBEEF;
+    state3.installer_text = "Error: The beef has died.";
+    state3.installer_cmd_line = "path/to/updater --crash-me";
+    state_update.Run(state3);
+
+    std::move(callback).Run(UpdateService::Result::kInstallFailed);
+  }
+};
+
+class UpdaterIPCTestCase : public testing::Test {
+ public:
+  static void SetUpTestSuite() { mojo::core::Init(); }
+
+  static void ExpectUpdateStatesEqual(const UpdateService::UpdateState& lhs,
+                                      const UpdateService::UpdateState& rhs) {
+    EXPECT_EQ(lhs.app_id, rhs.app_id);
+    EXPECT_EQ(lhs.state, rhs.state);
+    EXPECT_EQ(lhs.next_version.IsValid(), rhs.next_version.IsValid());
+    if (lhs.next_version.IsValid() && rhs.next_version.IsValid())
+      EXPECT_EQ(lhs.next_version, rhs.next_version);
+    EXPECT_EQ(lhs.downloaded_bytes, rhs.downloaded_bytes);
+    EXPECT_EQ(lhs.total_bytes, rhs.total_bytes);
+    EXPECT_EQ(lhs.install_progress, rhs.install_progress);
+    EXPECT_EQ(lhs.error_category, rhs.error_category);
+    EXPECT_EQ(lhs.error_code, rhs.error_code);
+    EXPECT_EQ(lhs.extra_code1, rhs.extra_code1);
+    EXPECT_EQ(lhs.installer_text, rhs.installer_text);
+    EXPECT_EQ(lhs.installer_cmd_line, rhs.installer_cmd_line);
+  }
+
+  void SetUp() override {
+    scoped_refptr<UpdateService> service =
+        base::MakeRefCounted<FakeUpdateService>();
+    mojo::Remote<mojom::UpdateService> remote;
+    service_stub_ = std::make_unique<UpdateServiceStub>(
+        remote.BindNewPipeAndPassReceiver(), std::move(service));
+    client_proxy_ =
+        CreateUpdateServiceProxy(UpdaterScope::kUser, std::move(remote));
+  }
+
+  UpdateService::StateChangeCallback ExpectUpdateStatesCallback() {
+    UpdateService::UpdateState state1;
+    state1.app_id = "ex1";
+    state1.state = UpdateService::UpdateState::State::kCheckingForUpdates;
+
+    UpdateService::UpdateState state2;
+    state2.app_id = "ex2";
+    state2.state = UpdateService::UpdateState::State::kDownloading;
+    state2.next_version = base::Version("3.14");
+    state2.downloaded_bytes = 1024;
+    state2.total_bytes = 2048;
+
+    UpdateService::UpdateState state3;
+    state3.app_id = "ex3";
+    state3.state = UpdateService::UpdateState::State::kUpdateError;
+    state3.install_progress = 99;
+    state3.error_code = 0xDEAD;
+    state3.extra_code1 = 0xBEEF;
+    state3.installer_text = "Error: The beef has died.";
+    state3.installer_cmd_line = "path/to/updater --crash-me";
+
+    std::vector<UpdateService::UpdateState> states = {state3, state2, state1};
+
+    return base::BindRepeating(
+        [](std::vector<UpdateService::UpdateState>& states,
+           const UpdateService::UpdateState& state) {
+          ASSERT_GT(states.size(), 0U);
+          ExpectUpdateStatesEqual(state, states.back());
+          states.pop_back();
+        },
+        base::OwnedRef(states));
+  }
+
+  UpdateService::Callback ExpectResultCallback() {
+    return base::BindOnce([](UpdateService::Result result) {
+             EXPECT_EQ(result, UpdateService::Result::kInstallFailed);
+           })
+        .Then(run_loop_.QuitClosure());
+  }
+
+ protected:
+  base::test::TaskEnvironment environment_;
+  base::RunLoop run_loop_;
+
+  std::unique_ptr<UpdateServiceStub> service_stub_;
+  scoped_refptr<UpdateService> client_proxy_;
+};
+
+TEST_F(UpdaterIPCTestCase, GetVersion) {
+  client_proxy_->GetVersion(base::BindOnce([](const base::Version& version) {
+                              EXPECT_EQ(version, base::Version("9"));
+                            }).Then(run_loop_.QuitClosure()));
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, FetchPolicies) {
+  client_proxy_->FetchPolicies(base::BindOnce([](int result) {
+                                 EXPECT_EQ(result, 42);
+                               }).Then(run_loop_.QuitClosure()));
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, RegisterApp) {
+  client_proxy_->RegisterApp({}, base::BindOnce([](int result) {
+                                   EXPECT_EQ(result, 42);
+                                 }).Then(run_loop_.QuitClosure()));
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, GetAppStates) {
+  client_proxy_->GetAppStates(
+      base::BindOnce([](const std::vector<UpdateService::AppState>&
+                            app_states) {
+        ASSERT_EQ(app_states.size(), 2U);
+
+        EXPECT_EQ(app_states[0].app_id, "ex1");
+        EXPECT_EQ(app_states[0].version, base::Version("9.19.20"));
+        EXPECT_EQ(app_states[0].ap, "foo");
+        EXPECT_EQ(app_states[0].brand_code, "FooBarInc");
+        EXPECT_EQ(app_states[0].brand_path, base::FilePath("/path/to/foo_bar"));
+        EXPECT_EQ(app_states[0].ecp, base::FilePath("path/to/foo_ecp"));
+
+        EXPECT_EQ(app_states[1].app_id, "ex2");
+        EXPECT_EQ(app_states[1].version, base::Version("98.4.5"));
+        EXPECT_EQ(app_states[1].ap, "zaz");
+        EXPECT_EQ(app_states[1].brand_code, "BazInc");
+        EXPECT_EQ(app_states[1].brand_path, base::FilePath("/path/to/baz"));
+        EXPECT_EQ(app_states[1].ecp, base::FilePath("path/to/baz_ecp"));
+      }).Then(run_loop_.QuitClosure()));
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, UpdateAll) {
+  client_proxy_->UpdateAll(ExpectUpdateStatesCallback(),
+                           ExpectResultCallback());
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, Update) {
+  client_proxy_->Update("ex1", "install_data_index",
+                        UpdateService::Priority::kBackground,
+                        UpdateService::PolicySameVersionUpdate::kAllowed,
+                        ExpectUpdateStatesCallback(), ExpectResultCallback());
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, Install) {
+  RegistrationRequest request;
+
+  client_proxy_->Install(request, "client_install_data", "install_data_index",
+                         UpdateService::Priority::kForeground,
+                         ExpectUpdateStatesCallback(), ExpectResultCallback());
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCTestCase, RunInstaller) {
+  RegistrationRequest request;
+
+  client_proxy_->RunInstaller("ex1", base::FilePath("/path/to/installer"),
+                              "install_args", "install_data",
+                              "install_settings", ExpectUpdateStatesCallback(),
+                              ExpectResultCallback());
+  run_loop_.Run();
+}
+
+class UpdaterIPCErrorTestCase : public UpdaterIPCTestCase {
+ public:
+  void SetUp() override {
+    // Create a Mojo Remote with a bound message pipe but without a receiver.
+    // This will cause RPC calls to eventually be dropped.
+    mojo::Remote<mojom::UpdateService> remote;
+    std::ignore = remote.BindNewPipeAndPassReceiver();
+    client_proxy_ =
+        CreateUpdateServiceProxy(UpdaterScope::kUser, std::move(remote));
+  }
+};
+
+TEST_F(UpdaterIPCErrorTestCase, DroppedGetVersion) {
+  client_proxy_->GetVersion(base::BindOnce([](const base::Version& version) {
+                              EXPECT_FALSE(version.IsValid());
+                            }).Then(run_loop_.QuitClosure()));
+
+  run_loop_.Run();
+}
+
+TEST_F(UpdaterIPCErrorTestCase, DroppedUpdateAll) {
+  client_proxy_->UpdateAll(
+      base::BindRepeating([](const UpdateService::UpdateState& state) {}),
+      base::BindOnce([](UpdateService::Result result) {
+        EXPECT_EQ(result, UpdateService::Result::kIPCConnectionFailed);
+      }).Then(run_loop_.QuitClosure()));
+
+  run_loop_.Run();
+}
+
+}  // namespace updater
diff --git a/chrome/updater/app/server/linux/update_service_stub.cc b/chrome/updater/app/server/linux/update_service_stub.cc
index fe70af0..6ce10e72 100644
--- a/chrome/updater/app/server/linux/update_service_stub.cc
+++ b/chrome/updater/app/server/linux/update_service_stub.cc
@@ -4,15 +4,94 @@
 
 #include "chrome/updater/app/server/linux/update_service_stub.h"
 
+#include <iterator>
 #include <utility>
 
-#include "base/callback.h"
+#include "base/check.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/version.h"
+#include "chrome/updater/app/server/linux/mojom/updater_service.mojom-forward.h"
+#include "chrome/updater/registration_data.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace updater {
+namespace {
+
+// Helper functions for converting between mojom types and their native
+// counterparts.
+[[nodiscard]] updater::RegistrationRequest MakeRegistrationRequest(
+    const mojom::RegistrationRequestPtr& mojom) {
+  CHECK(mojom);
+
+  updater::RegistrationRequest request;
+  request.app_id = mojom->app_id;
+  request.brand_code = mojom->brand_code;
+  request.brand_path = mojom->brand_path;
+  request.ap = mojom->ap;
+  request.version = base::Version(mojom->version);
+  request.existence_checker_path = mojom->existence_checker_path;
+  return request;
+}
+
+[[nodiscard]] mojom::AppStatePtr MakeMojoAppState(
+    const updater::UpdateService::AppState& app_state) {
+  return mojom::AppState::New(app_state.app_id, app_state.version.GetString(),
+                              app_state.ap, app_state.brand_code,
+                              app_state.brand_path, app_state.ecp);
+}
+
+[[nodiscard]] mojom::UpdateStatePtr MakeMojoUpdateState(
+    const updater::UpdateService::UpdateState& update_state) {
+  return mojom::UpdateState::New(
+      update_state.app_id,
+      static_cast<mojom::UpdateState::State>(update_state.state),
+      update_state.next_version.GetString(), update_state.downloaded_bytes,
+      update_state.total_bytes, update_state.install_progress,
+      static_cast<mojom::UpdateService::ErrorCategory>(
+          update_state.error_category),
+      update_state.error_code, update_state.extra_code1,
+      update_state.installer_text, update_state.installer_cmd_line);
+}
+
+// A thin wrapper around a StateChangeObserver remote to allow for refcounting.
+class StateChangeObserverWrapper
+    : public base::RefCountedThreadSafe<StateChangeObserverWrapper> {
+ public:
+  explicit StateChangeObserverWrapper(
+      std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer)
+      : observer_(std::move(observer)) {}
+
+  void OnStateChange(const updater::UpdateService::UpdateState& update_state) {
+    (*observer_)->OnStateChange(MakeMojoUpdateState(update_state));
+  }
+
+  void OnComplete(updater::UpdateService::Result result) {
+    (*observer_)->OnComplete(static_cast<mojom::UpdateService::Result>(result));
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<StateChangeObserverWrapper>;
+  virtual ~StateChangeObserverWrapper() = default;
+  std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer_;
+};
+
+// Binds a callback that forwards state change callbacks and the OnComplete
+// callback to a StateChangeObserver.
+[[nodiscard]] std::pair<UpdateService::StateChangeCallback,
+                        UpdateService::Callback>
+MakeStateChangeObserverCallbacks(
+    std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer) {
+  scoped_refptr<StateChangeObserverWrapper> wrapper =
+      base::MakeRefCounted<StateChangeObserverWrapper>(std::move(observer));
+  return {
+      base::BindRepeating(&StateChangeObserverWrapper::OnStateChange, wrapper),
+      base::BindOnce(&StateChangeObserverWrapper::OnComplete, wrapper)};
+}
+
+}  // namespace
 
 UpdateServiceStub::UpdateServiceStub(
     mojo::PendingReceiver<mojom::UpdateService> receiver,
@@ -29,4 +108,98 @@
       std::move(callback)));
 }
 
+void UpdateServiceStub::FetchPolicies(FetchPoliciesCallback callback) {
+  impl_->FetchPolicies(std::move(callback));
+}
+
+void UpdateServiceStub::RegisterApp(mojom::RegistrationRequestPtr request,
+                                    RegisterAppCallback callback) {
+  impl_->RegisterApp(MakeRegistrationRequest(request), std::move(callback));
+}
+
+void UpdateServiceStub::GetAppStates(GetAppStatesCallback callback) {
+  impl_->GetAppStates(
+      base::BindOnce([](const std::vector<updater::UpdateService::AppState>&
+                            app_states) {
+        std::vector<mojom::AppStatePtr> app_states_mojom;
+        std::transform(app_states.begin(), app_states.end(),
+                       std::back_inserter(app_states_mojom), &MakeMojoAppState);
+        return app_states_mojom;
+      }).Then(std::move(callback)));
+}
+
+void UpdateServiceStub::RunPeriodicTasks(RunPeriodicTasksCallback callback) {
+  impl_->RunPeriodicTasks(std::move(callback));
+}
+
+void UpdateServiceStub::UpdateAll(UpdateAllCallback callback) {
+  std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer =
+      std::make_unique<mojo::Remote<mojom::StateChangeObserver>>();
+  std::move(callback).Run(observer->BindNewPipeAndPassReceiver());
+
+  auto [state_change_callback, on_complete_callback] =
+      MakeStateChangeObserverCallbacks(std::move(observer));
+  impl_->UpdateAll(std::move(state_change_callback),
+                   std::move(on_complete_callback));
+}
+
+void UpdateServiceStub::Update(
+    const std::string& app_id,
+    const std::string& install_data_index,
+    UpdateService::Priority priority,
+    UpdateService::PolicySameVersionUpdate policy_same_version_update,
+    UpdateCallback callback) {
+  std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer =
+      std::make_unique<mojo::Remote<mojom::StateChangeObserver>>();
+  std::move(callback).Run(observer->BindNewPipeAndPassReceiver());
+
+  auto [state_change_callback, on_complete_callback] =
+      MakeStateChangeObserverCallbacks(std::move(observer));
+  impl_->Update(app_id, install_data_index,
+                static_cast<updater::UpdateService::Priority>(priority),
+                static_cast<updater::UpdateService::PolicySameVersionUpdate>(
+                    policy_same_version_update),
+                std::move(state_change_callback),
+                std::move(on_complete_callback));
+}
+
+void UpdateServiceStub::Install(mojom::RegistrationRequestPtr registration,
+                                const std::string& client_install_data,
+                                const std::string& install_data_index,
+                                UpdateService::Priority priority,
+                                InstallCallback callback) {
+  std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer =
+      std::make_unique<mojo::Remote<mojom::StateChangeObserver>>();
+  std::move(callback).Run(observer->BindNewPipeAndPassReceiver());
+
+  auto [state_change_callback, on_complete_callback] =
+      MakeStateChangeObserverCallbacks(std::move(observer));
+  impl_->Install(MakeRegistrationRequest(registration), client_install_data,
+                 install_data_index,
+                 static_cast<updater::UpdateService::Priority>(priority),
+                 std::move(state_change_callback),
+                 std::move(on_complete_callback));
+}
+
+void UpdateServiceStub::CancelInstalls(const std::string& app_id) {
+  impl_->CancelInstalls(app_id);
+}
+
+void UpdateServiceStub::RunInstaller(const std::string& app_id,
+                                     const ::base::FilePath& installer_path,
+                                     const std::string& install_args,
+                                     const std::string& install_data,
+                                     const std::string& install_settings,
+                                     RunInstallerCallback callback) {
+  std::unique_ptr<mojo::Remote<mojom::StateChangeObserver>> observer =
+      std::make_unique<mojo::Remote<mojom::StateChangeObserver>>();
+  std::move(callback).Run(observer->BindNewPipeAndPassReceiver());
+
+  auto [state_change_callback, on_complete_callback] =
+      MakeStateChangeObserverCallbacks(std::move(observer));
+  impl_->RunInstaller(app_id, installer_path, install_args, install_data,
+                      install_settings, std::move(state_change_callback),
+                      std::move(on_complete_callback));
+}
+
 }  // namespace updater
diff --git a/chrome/updater/app/server/linux/update_service_stub.h b/chrome/updater/app/server/linux/update_service_stub.h
index 5a39f02c..950953da 100644
--- a/chrome/updater/app/server/linux/update_service_stub.h
+++ b/chrome/updater/app/server/linux/update_service_stub.h
@@ -25,6 +25,29 @@
 
   // updater::mojom::UpdateService
   void GetVersion(GetVersionCallback callback) override;
+  void FetchPolicies(FetchPoliciesCallback callback) override;
+  void RegisterApp(mojom::RegistrationRequestPtr request,
+                   RegisterAppCallback callback) override;
+  void GetAppStates(GetAppStatesCallback callback) override;
+  void RunPeriodicTasks(RunPeriodicTasksCallback callback) override;
+  void UpdateAll(UpdateAllCallback callback) override;
+  void Update(const std::string& app_id,
+              const std::string& install_data_index,
+              UpdateService::Priority priority,
+              UpdateService::PolicySameVersionUpdate policy_same_version_update,
+              UpdateCallback callback) override;
+  void Install(mojom::RegistrationRequestPtr registration,
+               const std::string& client_install_data,
+               const std::string& install_data_index,
+               UpdateService::Priority priority,
+               InstallCallback callback) override;
+  void CancelInstalls(const std::string& app_id) override;
+  void RunInstaller(const std::string& app_id,
+                    const ::base::FilePath& installer_path,
+                    const std::string& install_args,
+                    const std::string& install_data,
+                    const std::string& install_settings,
+                    RunInstallerCallback callback) override;
 
  private:
   mojo::Receiver<updater::mojom::UpdateService> receiver_;
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index 0813a71..3172935 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -351,6 +351,9 @@
 
 inline constexpr int kErrorFailedToInstallLegacyUpdater = 34;
 
+// A Mojo remote was unexpectedly disconnected.
+inline constexpr int kErrorMojoDisconnect = 35;
+
 inline constexpr int kErrorTagParsing = 50;
 
 // Metainstaller errors.
diff --git a/chrome/updater/ipc/update_service_proxy_linux.cc b/chrome/updater/ipc/update_service_proxy_linux.cc
index 7b3bd537..604fe2f 100644
--- a/chrome/updater/ipc/update_service_proxy_linux.cc
+++ b/chrome/updater/ipc/update_service_proxy_linux.cc
@@ -8,27 +8,134 @@
 #include <string>
 #include <utility>
 
+#include "base/check.h"
+#include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/notreached.h"
-#include "base/time/time.h"
+#include "base/ranges/algorithm.h"
 #include "base/version.h"
 #include "chrome/updater/app/server/linux/mojom/updater_service.mojom.h"
+#include "chrome/updater/constants.h"
 #include "chrome/updater/linux/ipc_constants.h"
+#include "chrome/updater/registration_data.h"
 #include "chrome/updater/service_proxy_factory.h"
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace base {
+class TimeDelta;
+}
 
 namespace updater {
+namespace {
 
-// TODO(crbug.com/1276169) - implement.
+[[nodiscard]] UpdateService::UpdateState MakeUpdateState(
+    const mojom::UpdateStatePtr& state_mojom) {
+  updater::UpdateService::UpdateState state;
+  state.app_id = state_mojom->app_id;
+  state.state =
+      static_cast<UpdateService::UpdateState::State>(state_mojom->state);
+  state.next_version = base::Version(state_mojom->next_version);
+  state.downloaded_bytes = state_mojom->downloaded_bytes;
+  state.total_bytes = state_mojom->total_bytes;
+  state.install_progress = state_mojom->install_progress;
+  state.error_category =
+      static_cast<UpdateService::ErrorCategory>(state_mojom->error_category);
+  state.error_code = state_mojom->error_code;
+  state.extra_code1 = state_mojom->extra_code1;
+  state.installer_text = state_mojom->installer_text;
+  state.installer_cmd_line = state_mojom->installer_cmd_line;
+
+  return state;
+}
+
+[[nodiscard]] UpdateService::AppState MakeAppState(
+    const mojom::AppStatePtr& app_state_mojo) {
+  UpdateService::AppState app_state;
+  app_state.app_id = app_state_mojo->app_id;
+  app_state.version = base::Version(app_state_mojo->version);
+  app_state.ap = app_state_mojo->ap;
+  app_state.brand_code = app_state_mojo->brand_code;
+  app_state.brand_path = app_state_mojo->brand_path;
+  app_state.ecp = app_state_mojo->ecp;
+
+  return app_state;
+}
+
+[[nodiscard]] mojom::RegistrationRequestPtr MakeRegistrationRequest(
+    const RegistrationRequest& request) {
+  return mojom::RegistrationRequest::New(
+      request.app_id, request.brand_code, request.brand_path, request.ap,
+      request.version.GetString(), request.existence_checker_path);
+}
+
+class StateChangeObserverImpl : public mojom::StateChangeObserver {
+ public:
+  explicit StateChangeObserverImpl(
+      UpdateService::StateChangeCallback state_change_callback,
+      UpdateService::Callback complete_callback)
+      : state_change_callback_(std::move(state_change_callback)),
+        complete_callback_(std::move(complete_callback)) {}
+  StateChangeObserverImpl(const StateChangeObserverImpl&) = delete;
+  StateChangeObserverImpl& operator=(const StateChangeObserverImpl&) = delete;
+  ~StateChangeObserverImpl() override = default;
+
+  // Overrides for mojom::StateChangeObserver.
+  void OnStateChange(mojom::UpdateStatePtr state_mojom) override {
+    DCHECK(complete_callback_) << "OnStateChange received after OnComplete";
+    state_change_callback_.Run(MakeUpdateState(state_mojom));
+  }
+
+  void OnComplete(mojom::UpdateService::Result result) override {
+    DCHECK(complete_callback_) << "OnComplete received without a valid "
+                                  "callback. Was OnComplete run twice?";
+    if (complete_callback_) {
+      std::move(complete_callback_)
+          .Run(static_cast<updater::UpdateService::Result>(result));
+    }
+  }
+
+ private:
+  UpdateService::StateChangeCallback state_change_callback_;
+  UpdateService::Callback complete_callback_;
+};
+
+// Binds a callback which creates a self-owned StateChangeObserverImpl to
+// forward RPC callbacks to the provided native callbacks.
+[[nodiscard]] base::OnceCallback<
+    void(mojo::PendingReceiver<mojom::StateChangeObserver>)>
+MakeStateChangeObserver(
+    UpdateService::StateChangeCallback state_change_callback,
+    UpdateService::Callback complete_callback) {
+  complete_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+      std::move(complete_callback),
+      updater::UpdateService::Result::kIPCConnectionFailed);
+  return base::BindOnce(
+      [](UpdateService::StateChangeCallback state_change_callback,
+         UpdateService::Callback complete_callback,
+         mojo::PendingReceiver<mojom::StateChangeObserver> receiver) {
+        mojo::MakeSelfOwnedReceiver(
+            std::make_unique<StateChangeObserverImpl>(
+                std::move(state_change_callback), std::move(complete_callback)),
+            std::move(receiver));
+      },
+      std::move(state_change_callback), std::move(complete_callback));
+}
+
+}  // namespace
+
 class UpdateServiceProxyImpl
     : public base::RefCountedThreadSafe<UpdateServiceProxyImpl> {
  public:
@@ -48,55 +155,83 @@
   }
 
   void FetchPolicies(base::OnceCallback<void(int)> callback) {
-    NOTIMPLEMENTED();
+    remote_->FetchPolicies(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+        std::move(callback), kErrorMojoDisconnect));
   }
 
   void RegisterApp(const RegistrationRequest& request,
                    base::OnceCallback<void(int)> callback) {
-    NOTIMPLEMENTED();
+    remote_->RegisterApp(MakeRegistrationRequest(request),
+                         mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+                             std::move(callback), kErrorMojoDisconnect));
   }
 
   void GetAppStates(
       base::OnceCallback<void(const std::vector<UpdateService::AppState>&)>
           callback) {
-    NOTIMPLEMENTED();
+    remote_->GetAppStates(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+        base::BindOnce([](std::vector<mojom::AppStatePtr> app_states_mojo) {
+          std::vector<updater::UpdateService::AppState> app_states;
+          base::ranges::transform(
+              app_states_mojo, std::back_inserter(app_states), &MakeAppState);
+          return app_states;
+        }).Then(std::move(callback)),
+        std::vector<mojom::AppStatePtr>()));
   }
 
-  void RunPeriodicTasks(base::OnceClosure callback) { NOTIMPLEMENTED(); }
+  void RunPeriodicTasks(base::OnceClosure callback) {
+    remote_->RunPeriodicTasks(
+        mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback)));
+  }
 
-  void UpdateAll(UpdateService::StateChangeCallback state_update,
-                 UpdateService::Callback callback) {
-    NOTIMPLEMENTED();
+  void UpdateAll(UpdateService::StateChangeCallback state_change_callback,
+                 UpdateService::Callback complete_callback) {
+    remote_->UpdateAll(MakeStateChangeObserver(std::move(state_change_callback),
+                                               std::move(complete_callback)));
   }
 
   void Update(const std::string& app_id,
               const std::string& install_data_index,
               UpdateService::Priority priority,
               UpdateService::PolicySameVersionUpdate policy_same_version_update,
-              UpdateService::StateChangeCallback state_update,
-              UpdateService::Callback callback) {
-    NOTIMPLEMENTED();
+              UpdateService::StateChangeCallback state_change_callback,
+              UpdateService::Callback complete_callback) {
+    remote_->Update(app_id, install_data_index,
+                    static_cast<mojom::UpdateService::Priority>(priority),
+                    static_cast<mojom::UpdateService::PolicySameVersionUpdate>(
+                        policy_same_version_update),
+                    MakeStateChangeObserver(std::move(state_change_callback),
+                                            std::move(complete_callback)));
   }
 
   void Install(const RegistrationRequest& registration,
                const std::string& client_install_data,
                const std::string& install_data_index,
                UpdateService::Priority priority,
-               UpdateService::StateChangeCallback state_update,
-               UpdateService::Callback callback) {
-    NOTIMPLEMENTED();
+               UpdateService::StateChangeCallback state_change_callback,
+               UpdateService::Callback complete_callback) {
+    remote_->Install(MakeRegistrationRequest(registration), client_install_data,
+                     install_data_index,
+                     static_cast<mojom::UpdateService::Priority>(priority),
+                     MakeStateChangeObserver(std::move(state_change_callback),
+                                             std::move(complete_callback)));
   }
 
-  void CancelInstalls(const std::string& app_id) { NOTIMPLEMENTED(); }
+  void CancelInstalls(const std::string& app_id) {
+    remote_->CancelInstalls(app_id);
+  }
 
   void RunInstaller(const std::string& app_id,
                     const base::FilePath& installer_path,
                     const std::string& install_args,
                     const std::string& install_data,
                     const std::string& install_settings,
-                    UpdateService::StateChangeCallback state_update,
-                    UpdateService::Callback callback) {
-    NOTIMPLEMENTED();
+                    UpdateService::StateChangeCallback state_change_callback,
+                    UpdateService::Callback complete_callback) {
+    remote_->RunInstaller(
+        app_id, installer_path, install_args, install_data, install_settings,
+        MakeStateChangeObserver(std::move(state_change_callback),
+                                std::move(complete_callback)));
   }
 
  private:
diff --git a/chrome/updater/ipc/update_service_proxy_linux.h b/chrome/updater/ipc/update_service_proxy_linux.h
index d5f1026..a627f94 100644
--- a/chrome/updater/ipc/update_service_proxy_linux.h
+++ b/chrome/updater/ipc/update_service_proxy_linux.h
@@ -8,14 +8,20 @@
 #include "base/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
-#include "chrome/updater/app/server/linux/mojom/updater_service.mojom.h"
+#include "chrome/updater/app/server/linux/mojom/updater_service.mojom-forward.h"
 #include "chrome/updater/update_service.h"
-#include "chrome/updater/updater_scope.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
+namespace base {
+class FilePath;
+class Version;
+}  // namespace base
+
 namespace updater {
 
 class UpdateServiceProxyImpl;
+enum class UpdaterScope;
+struct RegistrationRequest;
 
 // All functions and callbacks must be called on the same sequence.
 class UpdateServiceProxy : public UpdateService {
diff --git a/chromeos/ash/services/libassistant/constants.cc b/chromeos/ash/services/libassistant/constants.cc
index 4d4fe8e..b0fa4f8 100644
--- a/chromeos/ash/services/libassistant/constants.cc
+++ b/chromeos/ash/services/libassistant/constants.cc
@@ -19,6 +19,9 @@
 const base::FilePath::CharType kAssistantBaseDirPath[] =
     FILE_PATH_LITERAL("/home/chronos/user/" ASSISTANT_DIR_STRING);
 
+const base::FilePath::CharType kLibAssistantSocketPath[] =
+    FILE_PATH_LITERAL("/run/libassistant");
+
 const char kLibAssistantDlcRootPath[] =
     "/run/imageloader/assistant-dlc/package/root";
 
diff --git a/chromeos/ash/services/libassistant/constants.h b/chromeos/ash/services/libassistant/constants.h
index 8c40e5a1..957a326 100644
--- a/chromeos/ash/services/libassistant/constants.h
+++ b/chromeos/ash/services/libassistant/constants.h
@@ -27,11 +27,9 @@
 COMPONENT_EXPORT(LIBASSISTANT_CONSTANTS)
 extern const base::FilePath::CharType kLibAssistantV2DlcPath[];
 
-#if !BUILDFLAG(IS_CHROMEOS_DEVICE)
 // A directory to save Libassistant socket files.
 COMPONENT_EXPORT(LIBASSISTANT_CONSTANTS)
 extern const base::FilePath::CharType kLibAssistantSocketPath[];
-#endif
 }  // namespace ash::libassistant
 
 #endif  // CHROMEOS_ASH_SERVICES_LIBASSISTANT_CONSTANTS_H_
diff --git a/chromeos/ash/services/libassistant/libassistant_loader_impl.cc b/chromeos/ash/services/libassistant/libassistant_loader_impl.cc
index 34c14323..64daa4c 100644
--- a/chromeos/ash/services/libassistant/libassistant_loader_impl.cc
+++ b/chromeos/ash/services/libassistant/libassistant_loader_impl.cc
@@ -107,16 +107,12 @@
   // Since we are not in the main thread, we can call the blocking method.
   DCHECK(!entry_point_);
 
-#if !BUILDFLAG(IS_CHROMEOS_DEVICE)
   // If the gRPC socket files exist, libassistant gRPC server could not start
   // because the binding to the new socket files will fail, with error message
   // that the files already exist.
-  // This cleanup is only needed for running the sandbox on gLinux.
-  // On a real device, these files will be cleaned up on the OS side when Chrome
-  // starts.
   DVLOG(3) << "Clean up temporary libassistant directory.";
   base::DeletePathRecursively(base::FilePath(kLibAssistantSocketPath));
-#endif
+
   base::FilePath path = GetLibassisantPath(root_path);
   base::ScopedNativeLibrary library = base::ScopedNativeLibrary(path);
   OnLibraryLoaded(std::move(library));
diff --git a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.cc b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.cc
index d70a9f6..174242b 100644
--- a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.cc
+++ b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.cc
@@ -16,10 +16,45 @@
 namespace ash {
 namespace quick_pair {
 
+namespace {
+
 constexpr int kBitsInByte = 8;
+
+// SASS enabled peripherals create their Bloom filters by changing the first
+// byte of either the account key in use or the most recently used account key
+// according to this spec:
+// https://developers.google.com/nearby/fast-pair/early-access/specifications/extensions/sass#SassInUseAccountKey
 constexpr uint8_t kRecentlyUsedByte = 0x05;
 constexpr uint8_t kInUseByte = 0x06;
 
+// Helper to AccountKeyFilter::IsAccountKeyInFilter().
+// Performs the test to see if |data| is in |bit_sets|, a Bloom filter.
+bool AccountKeyFilterTest(const std::vector<uint8_t>& data,
+                          const std::vector<uint8_t>& bit_sets) {
+  std::array<uint8_t, 32> hashed = crypto::SHA256Hash(data);
+
+  // Iterate over the hashed input in 4 byte increments, combine those 4
+  // bytes into an unsigned int and use it as the index into our
+  // |bit_sets|.
+  for (size_t i = 0; i < hashed.size(); i += 4) {
+    uint32_t hash = uint32_t{hashed[i]} << 24 | uint32_t{hashed[i + 1]} << 16 |
+                    uint32_t{hashed[i + 2]} << 8 | hashed[i + 3];
+
+    size_t num_bits = bit_sets.size() * kBitsInByte;
+    size_t n = hash % num_bits;
+    size_t byte_index = floor(n / kBitsInByte);
+    size_t bit_index = n % kBitsInByte;
+    bool is_set = (bit_sets[byte_index] >> bit_index) & 0x01;
+
+    if (!is_set)
+      return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
 AccountKeyFilter::AccountKeyFilter(
     const NotDiscoverableAdvertisement& advertisement)
     : bit_sets_(advertisement.account_key_filter) {
@@ -54,30 +89,7 @@
 AccountKeyFilter& AccountKeyFilter::operator=(AccountKeyFilter&&) = default;
 AccountKeyFilter::~AccountKeyFilter() = default;
 
-bool AccountKeyFilter::IsAccountKeyInFilter(std::vector<uint8_t> data) const {
-  std::array<uint8_t, 32> hashed = crypto::SHA256Hash(data);
-
-  // Iterate over the hashed input in 4 byte increments, combine those 4
-  // bytes into an unsigned int and use it as the index into our
-  // |bit_sets_|.
-  for (size_t i = 0; i < hashed.size(); i += 4) {
-    uint32_t hash = uint32_t{hashed[i]} << 24 | uint32_t{hashed[i + 1]} << 16 |
-                    uint32_t{hashed[i + 2]} << 8 | hashed[i + 3];
-
-    size_t num_bits = bit_sets_.size() * kBitsInByte;
-    size_t n = hash % num_bits;
-    size_t byte_index = floor(n / kBitsInByte);
-    size_t bit_index = n % kBitsInByte;
-    bool is_set = (bit_sets_[byte_index] >> bit_index) & 0x01;
-
-    if (!is_set)
-      return false;
-  }
-
-  return true;
-}
-
-bool AccountKeyFilter::Test(
+bool AccountKeyFilter::IsAccountKeyInFilter(
     const std::vector<uint8_t>& account_key_bytes) const {
   if (bit_sets_.empty())
     return false;
@@ -97,9 +109,9 @@
   std::vector<uint8_t> in_use_account_key(default_account_key);
   in_use_account_key[0] = kInUseByte;
 
-  return IsAccountKeyInFilter(default_account_key) ||
-         IsAccountKeyInFilter(in_use_account_key) ||
-         IsAccountKeyInFilter(recently_used_account_key);
+  return AccountKeyFilterTest(default_account_key, bit_sets_) ||
+         AccountKeyFilterTest(in_use_account_key, bit_sets_) ||
+         AccountKeyFilterTest(recently_used_account_key, bit_sets_);
 }
 
 }  // namespace quick_pair
diff --git a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h
index 470704aa..e59eaf7c 100644
--- a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h
+++ b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h
@@ -24,14 +24,10 @@
   AccountKeyFilter& operator=(AccountKeyFilter&&);
   ~AccountKeyFilter();
 
-  // Helper to Test, specifically tests whether hashed |data|
-  // is in the account key filter.
-  bool IsAccountKeyInFilter(std::vector<uint8_t> data) const;
-
   // Tests whether the |account_key_bytes| belong to this Account Key Filter.
   // Note: The return value may be a false positive, but will never be a false
   // negative.
-  bool Test(const std::vector<uint8_t>& account_key_bytes) const;
+  bool IsAccountKeyInFilter(const std::vector<uint8_t>& account_key_bytes) const;
 
  private:
   std::vector<uint8_t> bit_sets_;
diff --git a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter_unittest.cc b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter_unittest.cc
index 5785a0c6..1463a9d 100644
--- a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter_unittest.cc
+++ b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter_unittest.cc
@@ -34,6 +34,9 @@
 const std::vector<uint8_t> kSalt3{0x6C, 0xE5};
 const std::vector<uint8_t> kBatteryData3{0x33, 0xE4, 0xE4, 0x4C};
 
+// Values for SASS enabled Pixel Buds Pro that failed to subsequent pair prior
+// to ccrev.com/3953062 landing.
+// Value source: b/243855406#comment24.
 const std::vector<uint8_t> kAccountKey4{0x04, 0x3F, 0xC1, 0x8C, 0x63, 0xDC,
                                         0x75, 0x1A, 0xE8, 0x1A, 0xCF, 0x65,
                                         0x10, 0x15, 0x1D, 0xB0};
@@ -46,24 +49,24 @@
 
 TEST_F(AccountKeyFilterTest, EmptyFilter) {
   AccountKeyFilter filter({}, {});
-  EXPECT_FALSE(filter.Test(kAccountKey1));
-  EXPECT_FALSE(filter.Test(kAccountKey2));
+  EXPECT_FALSE(filter.IsAccountKeyInFilter(kAccountKey1));
+  EXPECT_FALSE(filter.IsAccountKeyInFilter(kAccountKey2));
 }
 
 TEST_F(AccountKeyFilterTest, EmptyVectorTest) {
   EXPECT_FALSE(
-      AccountKeyFilter(kFilterBytes1, {salt}).Test(std::vector<uint8_t>(0)));
+      AccountKeyFilter(kFilterBytes1, {salt}).IsAccountKeyInFilter(std::vector<uint8_t>(0)));
 }
 
 TEST_F(AccountKeyFilterTest, SingleAccountKey_AsBytes) {
-  EXPECT_TRUE(AccountKeyFilter(kFilterBytes1, {salt}).Test(kAccountKey1));
+  EXPECT_TRUE(AccountKeyFilter(kFilterBytes1, {salt}).IsAccountKeyInFilter(kAccountKey1));
 }
 
 TEST_F(AccountKeyFilterTest, TwoAccountKeys_AsBytes) {
   AccountKeyFilter filter(kFilterBytes1And2, {salt});
 
-  EXPECT_TRUE(filter.Test(kAccountKey1));
-  EXPECT_TRUE(filter.Test(kAccountKey2));
+  EXPECT_TRUE(filter.IsAccountKeyInFilter(kAccountKey1));
+  EXPECT_TRUE(filter.IsAccountKeyInFilter(kAccountKey2));
 }
 
 TEST_F(AccountKeyFilterTest, MissingAccountKey) {
@@ -71,8 +74,8 @@
                                          0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
                                          0xCC, 0xDD, 0xEE, 0xFF};
 
-  EXPECT_FALSE(AccountKeyFilter(kFilterBytes1, {salt}).Test(account_key));
-  EXPECT_FALSE(AccountKeyFilter(kFilterBytes1And2, {salt}).Test(account_key));
+  EXPECT_FALSE(AccountKeyFilter(kFilterBytes1, {salt}).IsAccountKeyInFilter(account_key));
+  EXPECT_FALSE(AccountKeyFilter(kFilterBytes1And2, {salt}).IsAccountKeyInFilter(account_key));
 }
 
 TEST_F(AccountKeyFilterTest, WithBatteryData) {
@@ -81,7 +84,7 @@
     salt_values.push_back(byte);
 
   EXPECT_TRUE(
-      AccountKeyFilter(kFilterWithBattery, salt_values).Test(kAccountKey1));
+      AccountKeyFilter(kFilterWithBattery, salt_values).IsAccountKeyInFilter(kAccountKey1));
 }
 
 TEST_F(AccountKeyFilterTest, TwoSaltBytes) {
@@ -92,17 +95,19 @@
   salt_values.insert(salt_values.end(), kBatteryData3.begin(),
                      kBatteryData3.end());
 
-  EXPECT_TRUE(AccountKeyFilter(kFilter3, salt_values).Test(kAccountKey3));
+  EXPECT_TRUE(AccountKeyFilter(kFilter3, salt_values).IsAccountKeyInFilter(kAccountKey3));
 }
 
+// Regression test for b/243855406.
 TEST_F(AccountKeyFilterTest, SassEnabledPeripheral) {
-  // Devices with battery data create account filters by concatenating data to
-  // end of salt bytes
+  // Account Key, Salt, and Battery Data values used are specific to an observed
+  // SASS device failure explained at the variable declaration for
+  // |kAccountKey4|, |kSalt4|, and |kBatteryData4|.
   std::vector<uint8_t> salt_values{};
   salt_values.insert(salt_values.end(), kSalt4.begin(), kSalt4.end());
   salt_values.insert(salt_values.end(), kBatteryData4.begin(),
                      kBatteryData4.end());
-  EXPECT_TRUE(AccountKeyFilter(kFilter4, salt_values).Test(kAccountKey4));
+  EXPECT_TRUE(AccountKeyFilter(kFilter4, salt_values).IsAccountKeyInFilter(kAccountKey4));
 }
 
 }  // namespace quick_pair
diff --git a/chromeos/crosapi/mojom/local_printer.mojom b/chromeos/crosapi/mojom/local_printer.mojom
index 4fe34ef..0ef61777 100644
--- a/chromeos/crosapi/mojom/local_printer.mojom
+++ b/chromeos/crosapi/mojom/local_printer.mojom
@@ -272,6 +272,20 @@
  kExtension,
 };
 
+// This union of structs represents the OAuth-related status of a printer.
+[Stable]
+struct OAuthNotNeeded {};
+[Stable]
+struct OAuthError {};
+[Stable]
+struct OAuthAccessToken { string token; };
+[Stable]
+union GetOAuthAccessTokenResult {
+  OAuthNotNeeded  none;
+  OAuthError error;
+  OAuthAccessToken token;
+};
+
 // This interface is used to query information about local printers
 // associated with the current Ash profile that can be used for
 // printing from Lacros.
@@ -339,4 +353,9 @@
   AddPrintJobObserver@13(
     pending_remote<PrintJobObserver> observer,
     PrintJobSource source) => ();
+
+  // Checks if the printer requires an OAuth. If yes, returns the access token.
+  [MinVersion=4]
+  GetOAuthAccessToken@14(string printer_id)
+    => (GetOAuthAccessTokenResult oauth_result);
 };
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index a417860..9cfd6d2 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-108-5359.6-1666607304-benchmark-108.0.5359.24-r1.orderfile.xz
+chromeos-chrome-orderfile-field-109-5344.0-1666002304-benchmark-109.0.5367.0-r1.orderfile.xz
diff --git a/chromeos/ui/frame/BUILD.gn b/chromeos/ui/frame/BUILD.gn
index fe437d4..59b8b6c5 100644
--- a/chromeos/ui/frame/BUILD.gn
+++ b/chromeos/ui/frame/BUILD.gn
@@ -34,6 +34,8 @@
     "frame_header.h",
     "frame_utils.cc",
     "frame_utils.h",
+    "header_view.cc",
+    "header_view.h",
     "highlight_border_overlay.cc",
     "highlight_border_overlay.h",
     "immersive/immersive_context.cc",
diff --git a/ash/frame/header_view.cc b/chromeos/ui/frame/header_view.cc
similarity index 89%
rename from ash/frame/header_view.cc
rename to chromeos/ui/frame/header_view.cc
index 014bcfb..3208ffa 100644
--- a/ash/frame/header_view.cc
+++ b/chromeos/ui/frame/header_view.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/frame/header_view.h"
+#include "chromeos/ui/frame/header_view.h"
 
 #include <memory>
+#include <vector>
 
-#include "ash/shell.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
-#include "ash/wm/window_state.h"
 #include "base/auto_reset.h"
+#include "chromeos/ui/base/tablet_state.h"
 #include "chromeos/ui/base/window_properties.h"
+#include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
 #include "chromeos/ui/frame/caption_buttons/frame_back_button.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
@@ -19,11 +19,12 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
+#include "ui/display/screen.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/non_client_view.h"
 
-namespace ash {
+namespace chromeos {
 
 using ::chromeos::DefaultFrameHeader;
 using ::chromeos::kFrameActiveColorKey;
@@ -35,7 +36,8 @@
 // as caption buttons.
 class HeaderView::HeaderContentView : public views::View {
  public:
-  HeaderContentView(HeaderView* header_view) : header_view_(header_view) {}
+  explicit HeaderContentView(HeaderView* header_view)
+      : header_view_(header_view) {}
 
   HeaderContentView(const HeaderContentView&) = delete;
   HeaderContentView& operator=(const HeaderContentView&) = delete;
@@ -84,12 +86,11 @@
 
   aura::Window* window = target_widget_->GetNativeWindow();
   window_observation_.Observe(window);
-  Shell::Get()->tablet_mode_controller()->AddObserver(this);
+  display::Screen::GetScreen()->AddObserver(this);
 }
 
 HeaderView::~HeaderView() {
-  if (Shell::Get()->tablet_mode_controller())
-    Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
+  display::Screen::GetScreen()->RemoveObserver(this);
 }
 
 void HeaderView::SchedulePaintForTitle() {
@@ -182,25 +183,6 @@
   return views::View::IsDrawn();
 }
 
-void HeaderView::OnTabletModeStarted() {
-  UpdateCaptionButtonsVisibility();
-  caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
-  parent()->Layout();
-  if (target_widget_ &&
-      Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
-          target_widget_)) {
-    target_widget_->non_client_view()->Layout();
-  }
-}
-
-void HeaderView::OnTabletModeEnded() {
-  UpdateCaptionButtonsVisibility();
-  caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
-  parent()->Layout();
-  if (target_widget_)
-    target_widget_->non_client_view()->Layout();
-}
-
 void HeaderView::OnWindowPropertyChanged(aura::Window* window,
                                          const void* key,
                                          intptr_t old) {
@@ -244,6 +226,30 @@
   }
 }
 
+void HeaderView::OnDisplayTabletStateChanged(display::TabletState state) {
+  switch (state) {
+    case display::TabletState::kInTabletMode:
+      UpdateCaptionButtonsVisibility();
+      caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
+      parent()->Layout();
+      if (target_widget_) {
+        target_widget_->non_client_view()->Layout();
+      }
+      break;
+    case display::TabletState::kInClamshellMode:
+      UpdateCaptionButtonsVisibility();
+      caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
+      parent()->Layout();
+      if (target_widget_)
+        target_widget_->non_client_view()->Layout();
+      break;
+    case display::TabletState::kEnteringTabletMode:
+      break;
+    case display::TabletState::kExitingTabletMode:
+      break;
+  }
+}
+
 views::View* HeaderView::avatar_icon() const {
   return avatar_icon_;
 }
@@ -373,4 +379,4 @@
 BEGIN_METADATA(HeaderView, views::View)
 END_METADATA
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/frame/header_view.h b/chromeos/ui/frame/header_view.h
similarity index 93%
rename from ash/frame/header_view.h
rename to chromeos/ui/frame/header_view.h
index bc0e493..b185c45e9 100644
--- a/ash/frame/header_view.h
+++ b/chromeos/ui/frame/header_view.h
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_FRAME_HEADER_VIEW_H_
-#define ASH_FRAME_HEADER_VIEW_H_
+#ifndef CHROMEOS_UI_FRAME_HEADER_VIEW_H_
+#define CHROMEOS_UI_FRAME_HEADER_VIEW_H_
 
 #include <memory>
+#include <utility>
+#include <vector>
 
-#include "ash/ash_export.h"
-#include "ash/public/cpp/tablet_mode_observer.h"
 #include "base/callback.h"
+#include "base/component_export.h"
 #include "base/scoped_observation.h"
 #include "chromeos/ui/frame/frame_header.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
@@ -20,11 +21,6 @@
 #include "ui/display/display_observer.h"
 #include "ui/views/view.h"
 
-namespace chromeos {
-class DefaultFrameHeader;
-class FrameCaptionButtonContainerView;
-}
-
 namespace gfx {
 class ImageSkia;
 }
@@ -34,18 +30,20 @@
 class ImageView;
 class Widget;
 class NonClientFrameView;
-}
+}  // namespace views
 
-namespace ash {
+namespace chromeos {
+
+class DefaultFrameHeader;
+class FrameCaptionButtonContainerView;
 
 enum class FrameBackButtonState;
 
 // View which paints the frame header (title, caption buttons...). It slides off
 // and on screen in immersive fullscreen.
-class ASH_EXPORT HeaderView
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) HeaderView
     : public views::View,
       public chromeos::ImmersiveFullscreenControllerDelegate,
-      public TabletModeObserver,
       public aura::WindowObserver,
       public display::DisplayObserver {
  public:
@@ -100,10 +98,6 @@
   void ChildPreferredSizeChanged(views::View* child) override;
   bool IsDrawn() const override;
 
-  // TabletModeObserver:
-  void OnTabletModeStarted() override;
-  void OnTabletModeEnded() override;
-
   // aura::WindowObserver:
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
@@ -113,6 +107,7 @@
   // display::DisplayObserver:
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
+  void OnDisplayTabletStateChanged(display::TabletState state) override;
 
   chromeos::FrameCaptionButtonContainerView* caption_button_container() {
     return caption_button_container_;
@@ -195,6 +190,6 @@
       window_observation_{this};
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_FRAME_HEADER_VIEW_H_
+#endif  // CHROMEOS_UI_FRAME_HEADER_VIEW_H_
diff --git a/components/desks_storage/core/desk_sync_bridge.cc b/components/desks_storage/core/desk_sync_bridge.cc
index f5a54c53..1600da6 100644
--- a/components/desks_storage/core/desk_sync_bridge.cc
+++ b/components/desks_storage/core/desk_sync_bridge.cc
@@ -12,7 +12,6 @@
 #include "base/guid.h"
 #include "base/json/json_writer.h"
 #include "base/memory/ptr_util.h"
-#include "base/notreached.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/components/desks_storage/core/desk_template_conversion.cc b/components/desks_storage/core/desk_template_conversion.cc
index 3a84fda6..efc6c43 100644
--- a/components/desks_storage/core/desk_template_conversion.cc
+++ b/components/desks_storage/core/desk_template_conversion.cc
@@ -991,7 +991,6 @@
     case ash::DeskTemplateType::kSaveAndRecall:
       return kDeskTypeSaveAndRecall;
     case ash::DeskTemplateType::kUnknown:
-      NOTREACHED();
       return kDeskTypeUnknown;
   }
 }
@@ -1850,8 +1849,8 @@
       out_entry_proto->set_desk_type(
           SyncDeskType::WorkspaceDeskSpecifics_DeskType_SAVE_AND_RECALL);
       return;
+    // Do nothing if type is unknown.
     case DeskTemplateType::kUnknown:
-      NOTREACHED();
       return;
   }
 }
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 211a123..260bb4d3 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
-#include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/frame/wide_frame_view.h"
 #include "ash/public/cpp/arc_resize_lock_type.h"
@@ -43,6 +42,7 @@
 #include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
 #include "chromeos/ui/frame/default_frame_header.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
@@ -1398,7 +1398,7 @@
     float dsf_to_default_dsf = device_scale_factor / scale_;
     width = base::ClampRound(shadow_bounds_->width() * dsf_to_default_dsf);
   }
-  static_cast<ash::HeaderView*>(GetFrameView()->GetHeaderView())
+  static_cast<chromeos::HeaderView*>(GetFrameView()->GetHeaderView())
       ->SetWidthInPixels(width);
 }
 
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index d478e7c..fa326c7 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -5,7 +5,6 @@
 #include "components/exo/client_controlled_shell_surface.h"
 
 #include "ash/display/screen_orientation_controller.h"
-#include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/frame/wide_frame_view.h"
 #include "ash/public/cpp/arc_resize_lock_type.h"
@@ -41,6 +40,7 @@
 #include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/header_view.h"
 #include "components/app_restore/full_restore_utils.h"
 #include "components/app_restore/window_properties.h"
 #include "components/exo/buffer.h"
@@ -1436,7 +1436,7 @@
       static_cast<ash::NonClientFrameViewAsh*>(
           shell_surface->GetWidget()->non_client_view()->frame_view());
   chromeos::FrameCaptionButtonContainerView* container =
-      static_cast<ash::HeaderView*>(frame_view->GetHeaderView())
+      static_cast<chromeos::HeaderView*>(frame_view->GetHeaderView())
           ->caption_button_container();
 
   // Visible
diff --git a/components/heap_profiling/in_process/BUILD.gn b/components/heap_profiling/in_process/BUILD.gn
index 9c25f7f..a58d45d 100644
--- a/components/heap_profiling/in_process/BUILD.gn
+++ b/components/heap_profiling/in_process/BUILD.gn
@@ -20,6 +20,7 @@
       "//components/metrics",
       "//components/metrics:child_call_stack_profile_builder",
       "//components/services/heap_profiling/public/cpp:cpp",
+      "//components/variations",
       "//components/version_info",
     ]
   }
@@ -41,6 +42,7 @@
       "//components/metrics",
       "//components/metrics:child_call_stack_profile_builder",
       "//components/metrics/public/mojom:call_stack_mojo_bindings",
+      "//components/variations",
       "//components/version_info",
       "//mojo/public/cpp/bindings",
     ]
diff --git a/components/heap_profiling/in_process/DEPS b/components/heap_profiling/in_process/DEPS
index 3a5daea..9425fd8c 100644
--- a/components/heap_profiling/in_process/DEPS
+++ b/components/heap_profiling/in_process/DEPS
@@ -2,6 +2,7 @@
   "+components/metrics/call_stack_profile_builder.h",
   "+components/metrics/call_stack_profile_params.h",
   "+components/services/heap_profiling/public",
+  "+components/variations/variations_switches.h",
   "+components/version_info",
 ]
 
diff --git a/components/heap_profiling/in_process/heap_profiler_parameters.cc b/components/heap_profiling/in_process/heap_profiler_parameters.cc
index c402ed1..b52e8b4d 100644
--- a/components/heap_profiling/in_process/heap_profiler_parameters.cc
+++ b/components/heap_profiling/in_process/heap_profiler_parameters.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_value_converter.h"
@@ -15,6 +16,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/metrics/call_stack_profile_params.h"
+#include "components/variations/variations_switches.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace heap_profiling {
@@ -164,9 +166,15 @@
 
   HeapProfilerParameters params = kDefaultHeapProfilerParameters;
 
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          variations::switches::kEnableBenchmarking) ||
+      !base::FeatureList::IsEnabled(kHeapProfilerReporting)) {
+    params.is_supported = false;
+    return params;
+  }
+
   // By default only the browser process is supported.
-  params.is_supported = base::FeatureList::IsEnabled(kHeapProfilerReporting) &&
-                        process_type == Process::kBrowser;
+  params.is_supported = (process_type == Process::kBrowser);
 
   // Override with field trial parameters if any are set.
   if (!params.UpdateFromJSON(kDefaultParameters.Get())) {
diff --git a/components/heap_profiling/in_process/heap_profiler_parameters_unittest.cc b/components/heap_profiling/in_process/heap_profiler_parameters_unittest.cc
index 03df531..2efa3bc 100644
--- a/components/heap_profiling/in_process/heap_profiler_parameters_unittest.cc
+++ b/components/heap_profiling/in_process/heap_profiler_parameters_unittest.cc
@@ -4,9 +4,11 @@
 
 #include "components/heap_profiling/in_process/heap_profiler_parameters.h"
 
+#include "base/command_line.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "components/metrics/call_stack_profile_params.h"
+#include "components/variations/variations_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -95,6 +97,25 @@
   EXPECT_FALSE(params.is_supported);
 }
 
+// Test that heap profiling is not supported for any process type when
+// --enable-benchmarking is specified on the command line.
+TEST(HeapProfilerParametersTest, EnableBenchmarking) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      variations::switches::kEnableBenchmarking);
+
+  using Process = metrics::CallStackProfileParams::Process;
+  EXPECT_FALSE(GetDefaultHeapProfilerParameters().is_supported);
+  EXPECT_FALSE(
+      GetHeapProfilerParametersForProcess(Process::kBrowser).is_supported);
+  EXPECT_FALSE(GetHeapProfilerParametersForProcess(Process::kGpu).is_supported);
+  EXPECT_FALSE(
+      GetHeapProfilerParametersForProcess(Process::kRenderer).is_supported);
+  EXPECT_FALSE(
+      GetHeapProfilerParametersForProcess(Process::kUtility).is_supported);
+  EXPECT_FALSE(GetHeapProfilerParametersForProcess(Process::kNetworkService)
+                   .is_supported);
+}
+
 TEST(HeapProfilerParametersTest, ApplyParameters) {
   constexpr char kDefaultParams[] = R"({
     "is-supported": false,
diff --git a/components/history_clusters/core/history_clusters_db_tasks.cc b/components/history_clusters/core/history_clusters_db_tasks.cc
index c2d9cfa0..c782ac6 100644
--- a/components/history_clusters/core/history_clusters_db_tasks.cc
+++ b/components/history_clusters/core/history_clusters_db_tasks.cc
@@ -173,10 +173,8 @@
             : false;
     if (is_clustered && recent_first_)
       continuation_params_.exhausted_unclustered_visits = true;
-    // Filter out visits from sync.
-    // TODO(manukh): Consider allowing the clustering backend to handle sync
-    //  visits.
-    if (!is_clustered && visit.source != history::SOURCE_SYNCED)
+
+    if (!is_clustered)
       annotated_visits_.push_back(std::move(visit));
   }
 
diff --git a/components/history_clusters/core/history_clusters_service_unittest.cc b/components/history_clusters/core/history_clusters_service_unittest.cc
index 5ba26595..81e7f82 100644
--- a/components/history_clusters/core/history_clusters_service_unittest.cc
+++ b/components/history_clusters/core/history_clusters_service_unittest.cc
@@ -213,9 +213,8 @@
     std::vector<history::AnnotatedVisit> visits =
         test_clustering_backend_->LastClusteredVisits();
 
-    // Visits 2, 3, and 5 are 1-day-old; visit 3 is a synced visit and therefore
-    // excluded.
-    ASSERT_EQ(visits.size(), 2u);
+    // Visits 2, 3, and 5 are 1-day-old; visit 3 is a synced visit.
+    ASSERT_EQ(visits.size(), 3u);
 
     auto& visit = visits[0];
     EXPECT_EQ(visit.visit_row.visit_id, 5);
@@ -226,6 +225,14 @@
     EXPECT_EQ(visit.context_annotations.page_end_reason, 5);
 
     visit = visits[1];
+    EXPECT_EQ(visit.visit_row.visit_id, 3);
+    EXPECT_EQ(visit.visit_row.visit_time,
+              GetHardcodedTestVisits()[2].visit_row.visit_time);
+    EXPECT_EQ(visit.visit_row.visit_duration, base::Seconds(20));
+    EXPECT_EQ(visit.url_row.url(), "https://synched-visit.com/");
+    EXPECT_EQ(visit.context_annotations.page_end_reason, 5);
+
+    visit = visits[2];
     EXPECT_EQ(visit.visit_row.visit_id, 2);
     EXPECT_EQ(visit.visit_row.visit_time,
               GetHardcodedTestVisits()[1].visit_row.visit_time);
@@ -432,13 +439,13 @@
   QueryClustersContinuationParams continuation_params = {};
   continuation_params.continuation_time = base::Time::Now();
 
-  // 1st query should return visits 2, 5, & 6, the good, 1-day-old visits.
-  // Visits 3, 0, and 10, also 1-day-old, are excluded since they're synced,
-  // missing history rows, and non-visible transition respectively.
+  // 1st query should return visits 2, 3, 5, & 6, the good, 1-day-old visits.
+  // Visit 0 is excluded because it's missing history rows. Visit 10 is excluded
+  // because it has a non-visible transition.
   {
     const auto [clusters, visits] = NextQueryClusters(continuation_params);
     EXPECT_THAT(GetClusterIds(clusters), testing::ElementsAre());
-    EXPECT_THAT(GetVisitIds(visits), testing::ElementsAre(5, 2, 6));
+    EXPECT_THAT(GetVisitIds(visits), testing::ElementsAre(5, 3, 2, 6));
     EXPECT_TRUE(continuation_params.is_continuation);
     EXPECT_FALSE(continuation_params.is_partial_day);
   }
@@ -695,16 +702,6 @@
   // Create 5 persisted visits with visit times 2, 1, 1, 60, and 1 days ago.
   AddHardcodedTestDataToHistoryService();
 
-  // Add a sync visit on a day without other visits in order to verify a day
-  // with only sync visits doesn't interrupt `GetAnnotatedVisitsToCluster`'s
-  // intention of iterating until a visit is found.
-  history::AnnotatedVisit sync_visit;
-  sync_visit.url_row.set_id(1);
-  sync_visit.visit_row.visit_id = 10;
-  sync_visit.visit_row.visit_time = base::Time::Now() - base::Days(15);
-  sync_visit.source = history::VisitSource::SOURCE_SYNCED;
-  AddCompleteVisit(sync_visit);
-
   // Helper to repeatedly schedule a `GetAnnotatedVisitsToCluster`, with the
   // continuation time returned from the previous task, and return the visits
   // it returns.
@@ -731,10 +728,10 @@
   }
   {
     // 3rd query should return the next oldest, 1-day-old visits. Visit 3 is
-    // excluded as it's from sync.
+    // is from sync, and is still included.
     const auto [clusters, visits] = NextVisits(continuation_params, false, 0);
     EXPECT_TRUE(clusters.empty());
-    EXPECT_THAT(GetVisitIds(visits), testing::ElementsAre(5, 2));
+    EXPECT_THAT(GetVisitIds(visits), testing::ElementsAre(5, 3, 2));
     EXPECT_TRUE(continuation_params.is_continuation);
     EXPECT_FALSE(continuation_params.exhausted_unclustered_visits);
     EXPECT_FALSE(continuation_params.exhausted_all_visits);
@@ -881,7 +878,7 @@
   histogram_tester.ExpectBucketCount(
       "History.Clusters.Backend.NumClustersReturned", 2, 1);
   histogram_tester.ExpectBucketCount(
-      "History.Clusters.Backend.NumVisitsToCluster", 2, 1);
+      "History.Clusters.Backend.NumVisitsToCluster", 3, 1);
   histogram_tester.ExpectTotalCount(
       "History.Clusters.Backend.GetMostRecentClusters."
       "ComputeClustersLatency",
diff --git a/components/infobars/core/infobar_delegate.cc b/components/infobars/core/infobar_delegate.cc
index 399556a..3b703d6b 100644
--- a/components/infobars/core/infobar_delegate.cc
+++ b/components/infobars/core/infobar_delegate.cc
@@ -40,7 +40,8 @@
 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
   const gfx::VectorIcon& vector_icon = GetVectorIcon();
   if (!vector_icon.is_empty())
-    return ui::ImageModel::FromVectorIcon(vector_icon, ui::kColorAccent, 20);
+    return ui::ImageModel::FromVectorIcon(vector_icon, ui::kColorInfoBarIcon,
+                                          20);
 #endif
 
   int icon_id = GetIconId();
diff --git a/components/module_installer/android/java/src/org/chromium/components/module_installer/engine/SplitCompatEngine.java b/components/module_installer/android/java/src/org/chromium/components/module_installer/engine/SplitCompatEngine.java
index bacb821..0c76f34 100644
--- a/components/module_installer/android/java/src/org/chromium/components/module_installer/engine/SplitCompatEngine.java
+++ b/components/module_installer/android/java/src/org/chromium/components/module_installer/engine/SplitCompatEngine.java
@@ -90,25 +90,27 @@
 
     private SplitInstallStateUpdatedListener getStatusUpdateListener() {
         return state -> {
-            if (state.moduleNames().size() != 1) {
-                throw new UnsupportedOperationException("Only one module supported.");
-            }
+            assert !state.moduleNames().isEmpty();
 
             int status = state.status();
-            String moduleName = state.moduleNames().get(0);
+            List<String> modules = state.moduleNames();
 
-            switch (status) {
-                case SplitInstallSessionStatus.INSTALLED:
-                    mFacade.updateCrashKeys();
-                    notifyListeners(moduleName, true);
-                    break;
-                case SplitInstallSessionStatus.FAILED:
-                    notifyListeners(moduleName, false);
-                    mFacade.getLogger().logStatusFailure(moduleName, state.errorCode());
-                    break;
+            if (status == SplitInstallSessionStatus.INSTALLED) {
+                mFacade.updateCrashKeys();
             }
 
-            mFacade.getLogger().logStatus(moduleName, status);
+            for (String moduleName : modules) {
+                switch (status) {
+                    case SplitInstallSessionStatus.INSTALLED:
+                        notifyListeners(moduleName, true);
+                        break;
+                    case SplitInstallSessionStatus.FAILED:
+                        notifyListeners(moduleName, false);
+                        mFacade.getLogger().logStatusFailure(moduleName, state.errorCode());
+                        break;
+                }
+                mFacade.getLogger().logStatus(moduleName, status);
+            }
         };
     }
 
@@ -118,6 +120,7 @@
         }
 
         sSessions.remove(moduleName);
+
         unregisterUpdateListener();
     }
 
diff --git a/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java b/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java
index d0bf3d0..a120fb0c 100644
--- a/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java
+++ b/components/module_installer/android/junit/src/org/chromium/components/module_installer/engine/SplitCompatEngineTest.java
@@ -209,82 +209,74 @@
         verify(mLogger, times(2)).logRequestStart(moduleName);
     }
 
-    @Test(expected = UnsupportedOperationException.class)
-    public void whenInstallingWithMoreThanOneModule_verifyException() {
-        // Arrange.
-        String moduleName = "whenInstallingWithMoreThanOneModule_verifyException";
-        InstallListener listener = mock(InstallListener.class);
-
-        // Mock SplitInstallSessionState.
-        SplitInstallSessionState state = mock(SplitInstallSessionState.class);
-        doReturn(Arrays.asList("m1", "m2")).when(state).moduleNames();
-
-        ArgumentCaptor<SplitInstallStateUpdatedListener> arg =
-                ArgumentCaptor.forClass(SplitInstallStateUpdatedListener.class);
-
-        // Act & Assert.
-        mInstaller.install(moduleName, listener);
-        verify(mManager).registerListener(arg.capture());
-        arg.getValue().onStateUpdate(state);
-    }
-
     @Test
     public void whenInstalled_verifyListenerAndLogger() {
         // Arrange.
-        String moduleName = "whenInstalled_verifyListenerAndLogger";
+        String moduleName1 = "whenInstalled_verifyListenerAndLogger1";
+        String moduleName2 = "whenInstalled_verifyListenerAndLogger2";
         Integer status = SplitInstallSessionStatus.INSTALLED;
-        InstallListener listener = mock(InstallListener.class);
+        InstallListener listener1 = mock(InstallListener.class);
+        InstallListener listener2 = mock(InstallListener.class);
 
         // Mock SplitInstallSessionState.
         SplitInstallSessionState state = mock(SplitInstallSessionState.class);
         doReturn(status).when(state).status();
-        doReturn(Arrays.asList(moduleName)).when(state).moduleNames();
+        doReturn(Arrays.asList(moduleName1, moduleName2)).when(state).moduleNames();
 
-        InOrder inOrder = inOrder(listener, mManager, mLogger, mInstallerFacade);
+        InOrder inOrder = inOrder(listener1, listener2, mManager, mLogger, mInstallerFacade);
         ArgumentCaptor<SplitInstallStateUpdatedListener> arg =
                 ArgumentCaptor.forClass(SplitInstallStateUpdatedListener.class);
 
         // Act.
-        mInstaller.install(moduleName, listener);
+        mInstaller.install(moduleName1, listener1);
+        mInstaller.install(moduleName2, listener2);
         verify(mManager).registerListener(arg.capture());
         arg.getValue().onStateUpdate(state);
 
         // Assert.
         inOrder.verify(mInstallerFacade, times(1)).updateCrashKeys();
-        inOrder.verify(listener, times(1)).onComplete(true);
+        inOrder.verify(listener1, times(1)).onComplete(true);
+        inOrder.verify(mLogger, times(1)).logStatus(moduleName1, status);
+        inOrder.verify(listener2, times(1)).onComplete(true);
         inOrder.verify(mManager, times(1)).unregisterListener(any());
-        inOrder.verify(mLogger, times(1)).logStatus(moduleName, status);
+        inOrder.verify(mLogger, times(1)).logStatus(moduleName2, status);
         inOrder.verifyNoMoreInteractions();
     }
 
     @Test
     public void whenFailureToInstall_verifyListenerAndLogger() {
         // Arrange.
-        String moduleName = "whenFailureToInstall_verifyListenerAndLogger";
+        String moduleName1 = "whenFailureToInstall_verifyListenerAndLogger1";
+        String moduleName2 = "whenFailureToInstall_verifyListenerAndLogger2";
         Integer status = SplitInstallSessionStatus.FAILED;
         Integer errorCode = SplitInstallErrorCode.NO_ERROR;
-        InstallListener listener = mock(InstallListener.class);
+        InstallListener listener1 = mock(InstallListener.class);
+        InstallListener listener2 = mock(InstallListener.class);
 
         // Mock SplitInstallSessionState.
         SplitInstallSessionState state = mock(SplitInstallSessionState.class);
         doReturn(status).when(state).status();
         doReturn(errorCode).when(state).errorCode();
-        doReturn(Arrays.asList(moduleName)).when(state).moduleNames();
+        doReturn(Arrays.asList(moduleName1, moduleName2)).when(state).moduleNames();
 
-        InOrder inOrder = inOrder(listener, mLogger, mManager);
+        InOrder inOrder = inOrder(listener1, listener2, mLogger, mManager);
         ArgumentCaptor<SplitInstallStateUpdatedListener> arg =
                 ArgumentCaptor.forClass(SplitInstallStateUpdatedListener.class);
 
         // Act.
-        mInstaller.install(moduleName, listener);
+        mInstaller.install(moduleName1, listener1);
+        mInstaller.install(moduleName2, listener2);
         verify(mManager).registerListener(arg.capture());
         arg.getValue().onStateUpdate(state);
 
         // Assert.
-        inOrder.verify(listener, times(1)).onComplete(false);
+        inOrder.verify(listener1, times(1)).onComplete(false);
+        inOrder.verify(mLogger, times(1)).logStatusFailure(moduleName1, errorCode);
+        inOrder.verify(mLogger, times(1)).logStatus(moduleName1, status);
+        inOrder.verify(listener2, times(1)).onComplete(false);
         inOrder.verify(mManager, times(1)).unregisterListener(any());
-        inOrder.verify(mLogger, times(1)).logStatusFailure(moduleName, errorCode);
-        inOrder.verify(mLogger, times(1)).logStatus(moduleName, status);
+        inOrder.verify(mLogger, times(1)).logStatusFailure(moduleName2, errorCode);
+        inOrder.verify(mLogger, times(1)).logStatus(moduleName2, status);
         inOrder.verifyNoMoreInteractions();
     }
 
diff --git a/components/reporting/storage/storage.cc b/components/reporting/storage/storage.cc
index 32aca65..701db8d7 100644
--- a/components/reporting/storage/storage.cc
+++ b/components/reporting/storage/storage.cc
@@ -486,19 +486,14 @@
         dir_enum,
         base::BindRepeating(
             [](uint64_t new_file_index, const base::FilePath& full_name) {
-              const auto extension = full_name.Extension();
-              if (extension.empty()) {
-                // Should not happen, will remove this file.
-                return true;
-              }
-              uint64_t file_index = 0;
-              if (!base::StringToUint64(extension.substr(1), &file_index)) {
-                // Bad extension - not a number. Should not happen, will remove
-                // this file.
-                return true;
-              }
-              if (file_index < new_file_index) {
-                // Lower index file, will remove it.
+              const auto file_index =
+                  StorageQueue::GetFileSequenceIdFromPath(full_name);
+              if (!file_index.ok() ||  // Should not happen, will remove file.
+                  file_index.ValueOrDie() <
+                      static_cast<int64_t>(
+                          new_file_index)) {  // Lower index file, will remove
+                                              // it.
+
                 return true;
               }
               return false;
@@ -522,25 +517,24 @@
         // Duplicate file name. Should not happen.
         continue;
       }
-      const auto extension = full_name.Extension();
-      if (extension.empty()) {
-        // Should not happen.
+      const auto file_index =
+          StorageQueue::GetFileSequenceIdFromPath(full_name);
+      if (!file_index.ok()) {  // Shouldn't happen, something went wrong.
         continue;
       }
-      uint64_t file_index = 0;
-      bool success = base::StringToUint64(extension.substr(1), &file_index);
-      if (!success) {
-        // Bad extension - not a number. Should not happen (file is corrupt).
-        continue;
-      }
-      if (!found_key_files->emplace(file_index, full_name).second) {
+      if (!found_key_files
+               ->emplace(static_cast<uint64_t>(file_index.ValueOrDie()),
+                         full_name)
+               .second) {
         // Duplicate extension (e.g., 01 and 001). Should not happen (file is
         // corrupt).
         continue;
       }
       // Set 'next_key_file_index_' to a number which is definitely not used.
-      if (next_key_file_index_.load() <= file_index) {
-        next_key_file_index_.store(file_index + 1);
+      if (static_cast<int64_t>(next_key_file_index_.load()) <=
+          file_index.ValueOrDie()) {
+        next_key_file_index_.store(
+            static_cast<uint64_t>(file_index.ValueOrDie() + 1));
       }
     }
   }
diff --git a/components/reporting/storage/storage_queue.cc b/components/reporting/storage/storage_queue.cc
index beda370..9567376 100644
--- a/components/reporting/storage/storage_queue.cc
+++ b/components/reporting/storage/storage_queue.cc
@@ -304,11 +304,31 @@
   return Status::StatusOK();
 }
 
+StatusOr<int64_t> StorageQueue::GetFileSequenceIdFromPath(
+    const base::FilePath& file_name) {
+  const auto extension = file_name.FinalExtension();
+  if (extension.empty() || extension == FILE_PATH_LITERAL(".")) {
+    return Status(error::INTERNAL,
+                  base::StrCat({"File has no extension: '",
+                                file_name.MaybeAsASCII(), "'"}));
+  }
+  int64_t file_sequence_id = 0;
+  const bool success =
+      base::StringToInt64(extension.substr(1), &file_sequence_id);
+  if (!success) {
+    return Status(error::INTERNAL,
+                  base::StrCat({"File extension does not parse: '",
+                                file_name.MaybeAsASCII(), "'"}));
+  }
+
+  return file_sequence_id;
+}
+
 StatusOr<int64_t> StorageQueue::AddDataFile(
     const base::FilePath& full_name,
     const base::FileEnumerator::FileInfo& file_info) {
   ASSIGN_OR_RETURN(int64_t file_sequence_id,
-                   SingleFile::GetFileSequenceIdFromPath(full_name));
+                   GetFileSequenceIdFromPath(full_name));
   RETURN_IF_ERROR(SetGenerationId(full_name));
 
   auto file_or_status = SingleFile::Create(full_name, file_info.GetSize(),
@@ -723,7 +743,7 @@
   for (auto full_name = dir_enum.Next(); !full_name.empty();
        full_name = dir_enum.Next()) {
     const auto file_sequence_id =
-        SingleFile::GetFileSequenceIdFromPath(dir_enum.GetInfo().GetName());
+        GetFileSequenceIdFromPath(dir_enum.GetInfo().GetName());
     if (!file_sequence_id.ok()) {
       continue;
     }
@@ -796,8 +816,7 @@
       dir_enum,
       base::BindRepeating(
           [](int64_t sequence_id_to_keep, const base::FilePath& full_name) {
-            const auto sequence_id =
-                SingleFile::GetFileSequenceIdFromPath(full_name);
+            const auto sequence_id = GetFileSequenceIdFromPath(full_name);
             if (!sequence_id.ok()) {
               return false;
             }
@@ -1987,26 +2006,6 @@
       new SingleFile(filename, size, memory_resource, disk_space_resource));
 }
 
-StatusOr<int64_t> StorageQueue::SingleFile::GetFileSequenceIdFromPath(
-    const base::FilePath& file_name) {
-  const auto extension = file_name.FinalExtension();
-  if (extension.empty() || extension == FILE_PATH_LITERAL(".")) {
-    return Status(error::INTERNAL,
-                  base::StrCat({"File has no extension: '",
-                                file_name.MaybeAsASCII(), "'"}));
-  }
-  int64_t file_sequence_id = 0;
-  const bool success =
-      base::StringToInt64(extension.substr(1), &file_sequence_id);
-  if (!success) {
-    return Status(error::INTERNAL,
-                  base::StrCat({"File extension does not parse: '",
-                                file_name.MaybeAsASCII(), "'"}));
-  }
-
-  return file_sequence_id;
-}
-
 StorageQueue::SingleFile::SingleFile(
     const base::FilePath& filename,
     int64_t size,
diff --git a/components/reporting/storage/storage_queue.h b/components/reporting/storage/storage_queue.h
index 672ff20..78a7295 100644
--- a/components/reporting/storage/storage_queue.h
+++ b/components/reporting/storage/storage_queue.h
@@ -136,6 +136,12 @@
   // Access queue options.
   const QueueOptions& options() const { return options_; }
 
+  // Returns the file sequence ID (the first sequence ID in the file) if the
+  // sequence ID can be extracted from the extension. Otherwise, returns an
+  // error status.
+  static StatusOr<int64_t> GetFileSequenceIdFromPath(
+      const base::FilePath& file_name);
+
  protected:
   virtual ~StorageQueue();
 
@@ -161,12 +167,6 @@
         scoped_refptr<ResourceInterface> memory_resource,
         scoped_refptr<ResourceInterface> disk_space_resource);
 
-    // Returns the file sequence ID (the first sequence ID in the file) if the
-    // sequence ID can be extracted from the extension. Otherwise, returns an
-    // error status.
-    static StatusOr<int64_t> GetFileSequenceIdFromPath(
-        const base::FilePath& file_name);
-
     Status Open(bool read_only);  // No-op if already opened.
     void Close();                 // No-op if not opened.
 
diff --git a/components/security_interstitials/content/utils.cc b/components/security_interstitials/content/utils.cc
index 7a4cd19..7ad8cfc 100644
--- a/components/security_interstitials/content/utils.cc
+++ b/components/security_interstitials/content/utils.cc
@@ -20,6 +20,10 @@
 #include "components/security_interstitials/content/android/jni_headers/DateAndTimeSettingsHelper_jni.h"
 #endif
 
+#if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
+#endif
+
 #if BUILDFLAG(IS_WIN)
 #include "base/base_paths_win.h"
 #include "base/path_service.h"
@@ -74,13 +78,8 @@
   options.allow_new_privs = true;
   base::LaunchProcess(command, options);
 
-#elif BUILDFLAG(IS_APPLE)
-  base::CommandLine command(base::FilePath("/usr/bin/open"));
-  command.AppendArg("/System/Library/PreferencePanes/DateAndTime.prefPane");
-
-  base::LaunchOptions options;
-  options.wait = false;
-  base::LaunchProcess(command, options);
+#elif BUILDFLAG(IS_MAC)
+  base::mac::OpenSystemSettingsPane(base::mac::SystemSettingsPane::kDateTime);
 
 #elif BUILDFLAG(IS_WIN)
   base::FilePath path;
diff --git a/components/test/data/webcrypto/bad_rsa_keys.json b/components/test/data/webcrypto/bad_rsa_keys.json
deleted file mode 100644
index 846cd20..0000000
--- a/components/test/data/webcrypto/bad_rsa_keys.json
+++ /dev/null
@@ -1,124 +0,0 @@
-// This file contains incorrect key data in a variety of formats.
-//   "key" -- either a dictionary for JWK, or hex encoded bytes for PKCS8/SPKI.
-//   "key_format" -- one of: "jwk", "pkcs8", "spki"
-//   "error" -- The expected rejection reason when importing the key.
-[
-  // The provided SPKI is empty.
-  {
-    "key_format": "spki",
-    "key": "",
-    "error": "DataError"
-  },
-
-  // The provided PKCS8 is empty.
-  {
-    "key_format": "pkcs8",
-    "key": "",
-    "error": "DataError"
-  },
-
-  // Bad DER encoding for SPKI.
-  {
-    "key_format": "spki",
-    "key": "618333c4cb",
-    "error": "DataError"
-  },
-
-  // Bad DER encoding for PKCS8.
-  {
-    "key_format": "pkcs8",
-    "key": "618333c4cb",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 50 was inverted, which is inside of "n")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7E5741D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 168 was inverted, which is inside of "e")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301FF0102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 175 was inverted, which is inside of "d")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5FB2A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 333 was inverted, which is inside of "p")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC697948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 373 was inverted, which is inside of "q")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69D351CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 450 was inverted, which is inside of "dp")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACAA572EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 550 was inverted, which is inside of "dq")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E16782557D7580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // Corrupted PKCS8 (byte index 600 was inverted, which is inside of "qi")
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319A74B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D",
-    "error": "DataError"
-  },
-
-  // PKCS8 with only n, e, d parameters supplied, and all others are 0
-  {
-    "key_format": "pkcs8",
-    "key": "30820138020100300D06092A864886F70D0101010500048201223082011E02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325020100020100020100020100020100",
-    "error": "DataError"
-  },
-
-  // Valid PKCS8, however it defines an EC key not an RSA key.
-  {
-    "key_format": "pkcs8",
-    "key": "308187020100301306072A8648CE3D020106082A8648CE3D030107046D306B02010104201FE33950C5F461124AE992C2BDFDF1C73B1615F571BD567E60D19AA1F48CDF42A144034200047C110C66DCFDA807F6E69E45DDB3C74F69A1484D203E8DC5ADA8E9A9DD7CB3C70DF448986E51BDE5D1576F99901F9C2C6A806A47FD907643A72B835597EFC8C6",
-    "error": "DataError"
-  },
-
-  // Valid SPKI, however it defines an EC key not an RSA key.
-  {
-    "key_format": "spki",
-    "key": "3059301306072A8648CE3D020106082A8648CE3D030107034200049CB0CF69303DAFC761D4E4687B4ECF039E6D34AB964AF80810D8D558A4A8D6F72D51233A1788920A86EE08A1962C79EFA317FB7879E297DAD2146DB995FA1C78",
-    "error": "DataError"
-  },
-
-  // PKCS8 with extra data after it.
-  {
-    "key_format": "pkcs8",
-    "key": "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000102818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE55FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBCCCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F19D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA13938655BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C077790427BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA712049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455EAEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA5BA8D8584194EB2190ACF832B847F13A3D24A79F4D00000000",
-    "error": "DataError"
-  },
-
-  // SPKI with extra data after it.
-  {
-    "key_format": "spki",
-    "key": "30819F300D06092A864886F70D010101050003818D0030818902818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000100000000",
-    "error": "DataError"
-  }
-]
diff --git a/components/test/data/webcrypto/pkcs1v15_sign.json b/components/test/data/webcrypto/pkcs1v15_sign.json
deleted file mode 100644
index 05e1ea1..0000000
--- a/components/test/data/webcrypto/pkcs1v15_sign.json
+++ /dev/null
@@ -1,109 +0,0 @@
-// Use the NIST test vectors from Example 1 of
-// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt
-// These vectors are known answers for RSA PKCS#1 v1.5 Signature with a SHA-1
-// digest, using a predefined key pair.
-
-[
-  // The following data are the input messages and corresponding computed RSA
-  // PKCS#1 v1.5 signatures from the NIST link above.
-  // PKCS#1 v1.5 Signature Example 1.1
-  {
-    "message_hex": "cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0",
-    "signature_hex": "6bc3a06656842930a247e30d5864b4d819236ba7c68965862ad7dbc4e24af28e86bb531f03358be5fb74777c6086f850caef893f0d6fcc2d0c91ec013693b4ea00b80cd49aac4ecb5f8911afe539ada4a8f3823d1d13e472d1490547c659c7617f3d24087ddb6f2b72096167fc097cab18e9a458fcb634cdce8ee35894c484d7"
-  },
-  // PKCS#1 v1.5 Signature Example 1.2
-  {
-    "message_hex": "851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e",
-    "signature_hex": "84fd2ce734ec1da828d0f15bf49a8707c15d05948136de537a3db421384167c86fae022587ee9e137daee754738262932d271c744c6d3a189ad4311bdb020492e322fbddc40406ea860d4e8ea2a4084aa98b9622a446756fdb740ddb3d91db7670e211661bbf8709b11c08a70771422d1a12def29f0688a192aebd89e0f896f8"
-  },
-  // PKCS#1 v1.5 Signature Example1.3
-  {
-    "message_hex": "a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd04ef8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be8191cf3955fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e81ca2cab31c745e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3ce27fae1d6cb20f4564d66ce4767583d0e5f060215b59017be85ea848939127bd8c9c4d47b51056c031cf336f17c9980f3b8f5b9b6878e8b797aa43b882684333e17893fe9caa6aa299f7ed1a18ee2c54864b7b2b99b72618fb02574d139ef50f019c9eef416971338e7d470",
-    "signature_hex": "0b1f2e5180e5c7b4b5e672929f664c4896e50c35134b6de4d5a934252a3a245ff48340920e1034b7d5a5b524eb0e1cf12befef49b27b732d2c19e1c43217d6e1417381111a1d36de6375cf455b3c9812639dbc27600c751994fb61799ecf7da6bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2"
-  },
-  // PKCS#1 v1.5 Signature Example 1.4
-  {
-    "message_hex": "bc656747fa9eafb3f0",
-    "signature_hex": "45607ad611cf5747a41ac94d0ffec878bdaf63f6b57a4b088bf36e34e109f840f24b742ada16102dabf951cbc44f8982e94ed4cd09448d20ec0efa73545f80b65406bed6194a61c340b4ad1568cbb75851049f11af1734964076e02029aee200e40e80be0f4361f69841c4f92a4450a2286d43289b405554c54d25c6ecb584f4"
-  },
-  // PKCS#1 v1.5 Signature Example 1.5
-  {
-    "message_hex": "b45581547e5427770c768e8b82b75564e0ea4e9c32594d6bff706544de0a8776c7a80b4576550eee1b2acabc7e8b7d3ef7bb5b03e462c11047eadd00629ae575480ac1470fe046f13a2bf5af17921dc4b0aa8b02bee6334911651d7f8525d10f32b51d33be520d3ddf5a709955a3dfe78283b9e0ab54046d150c177f037fdccc5be4ea5f68b5e5a38c9d7edcccc4975f455a6909b4",
-    "signature_hex": "54be9d90877515f450279c15b5f61ad6f15ecc95f18cbed82b65b1667a575809587994668044f3bc2ae7f884501f64f0b43f588cfa205a6ab704328c2d4ab92a7ae13440614d3e085f401da9ad28e2105e4a0edb681a6424df047388ce051ee9df7bc2163fe347520ad51ccd518064383e741acad3cbdc2cb5a7c68e868464c2"
-  },
-  // PKCS#1 v1.5 Signature Example 1.6
-  {
-    "message_hex": "10aae9a0ab0b595d0841207b700d48d75faedde3b775cd6b4cc88ae06e4694ec74ba18f8520d4f5ea69cbbe7cc2beba43efdc10215ac4eb32dc302a1f53dc6c4352267e7936cfebf7c8d67035784a3909fa859c7b7b59b8e39c5c2349f1886b705a30267d402f7486ab4f58cad5d69adb17ab8cd0ce1caf5025af4ae24b1fb8794c6070cc09a51e2f9911311e3877d0044c71c57a993395008806b723ac38373d395481818528c1e7053739282053529510e935cd0fa77b8fa53cc2d474bd4fb3cc5c672d6ffdc90a00f9848712c4bcfe46c60573659b11e6457e861f0f604b6138d144f8ce4e2da73",
-    "signature_hex": "0e6ff63a856b9cbd5dbe423183122047dd39d6f76d1b2310e546fe9ee73b33efa7c78f9474455c9e5b88cb383aafc3698668e7b7a59a9cbb5b0897b6c5afb7f8bac4b924e98d760a15fc43d2814ab2d5187f79bed9915a93397ebc22a7677506a02e076d3ffdc0441dbd4db00453dc28d830e0573f77b817b505c38b4a4bb5d0"
-  },
-  // PKCS#1 v1.5 Signature Example 1.7
-  {
-    "message_hex": "efb5da1b4d1e6d9a5dff92d0184da7e31f877d1281ddda625664869e8379e67ad3b75eae74a580e9827abd6eb7a002cb5411f5266797768fb8e95ae40e3e8b3466f5ab15d69553952939ec23e61d58497fac76aa1c0bb5a3cb4a54383587c7bb78d13eefda205443e6ce4365802df55c64713497984e7ca96722b3edf84d56",
-    "signature_hex": "8385d58533a995f72df262b70f40b391ddf515f464b9d2cc2d66398fc05689d811632946d62eabdca7a31fcf6cd6c981d28bbc29083e4a6d5b2b378ca4e540f060b96d53ad2693f82178b94e2e2f86b9accfa02025107e062ab7080175684501028f676461d81c008fe4750671649970878fc175cf98e96b2ecbf6874d77dacb"
-  },
-  // PKCS#1 v1.5 Signature Example 1.8
-  {
-    "message_hex": "53bb58ce42f1984940552657233b14969af365c0a561a4132af18af39432280e3e437082434b19231837184f02cf2b2e726bebf74d7ae3256d8b72f3eafdb134d33de06f2991d299d59f5468d43b9958d6a968f5969edbbc6e7185cbc716c7c945dafa9cc71ddfaaa01094a452ddf5e2407320400bf05ea9729cafbf0600e78807ef9462e3fde32ed7d981a56f4751ef64fb4549910ecc911d728053b39943004740e6f5821fe8d75c0617bf2c6b24bbfc34013fc95f0dedf5ba297f504fb833da2a436d1d8ff1cc5193e2a64389fced918e7feb6716330f66801db9497549cf1d3bd97cf1bc6255",
-    "signature_hex": "8e1f3d26ec7c6bbb8c54c5d25f3120587803af6d3c2b99a37ced6a3657d4ae54266f63fffde660c866d65d0ab0589e1d12d9ce6054b05c8668ae127171ccaae7f1cd409677f52157b6123ab227f27a00966d1439b42a32169d1070394026fc8bc93545b1ac252d0f7da751c02e33a47831fbd71514c2bbbd3adb6740c0fd68ad"
-  },
-  // PKCS#1 v1.5 Signature Example 1.9
-  {
-    "message_hex": "27cadc698450945f204ec3cf8c6cbd8ceb4cc0cbe312274fa96b04deac855160c0e04e4ac5d38210c27c",
-    "signature_hex": "7b63f9223356f35f6117f68c8f8220034fc2384ab5dc6904141f139314d6ee89f54ec6ffd18c413a23c5931c7fbb13c555ccfd590e0eaa853c8c94d2520cd4250d9a05a193b65dc749b82478af0156ee1de55ddad33ec1f0099cad6c891a3617c7393d05fbfbbb00528a001df0b204ebdf1a341090dea89f870a877458427f7b"
-  },
-  // PKCS#1 v1.5 Signature Example 1.10
-  {
-    "message_hex": "716407e901b9ef92d761b013fd13eb7ad72aed",
-    "signature_hex": "2a22dbe3774d5b297201b55a0f17f42dce63b7845cb325cfe951d0badb5c5a14472143d896c86cc339f83671164215abc97862f2151654e75a3b357c37311b3d7268cab540202e23bee52736f2cd86cce0c7dbde95e1c600a47395dc5eb0a472153fbc4fb21b643e0c04ae14dd37e97e617a7567c89652219781001ba6f83298"
-  },
-  // PKCS#1 v1.5 Signature Example 1.11
-  {
-    "message_hex": "46c24e4103001629c712dd4ce8d747ee595d6c744ccc4f71347d9b8abf49d1b8fb2ef91b95dc899d4c0e3d2997e638f4cf3f68e0498de5aabd13f0dfe02ff26ba4379104e78ffa95ffbd15067ef8cbd7eb7860fecc71abe13d5c720a66851f2defd4e795054d7bec024bb422a46a7368b56d95b47aebafbeadd612812593a70db9f96d451ee15edb299308d777f4bb68ed3377c32156b41b7a9c92a14c8b81144399c56a5a432f4f770aa97da8415d0bda2e813206031e70620031c881d616bffd5f03bf147c1e73766c26246208",
-    "signature_hex": "12235b0b406126d9d260d447e923a11051fb243079f446fd73a70181d53634d7a0968e4ee27777eda63f6e4a3a91ad5985998a4848da59ce697b24bb332fa2ad9ce462ca4affdc21dab908e8ce15af6eb9105b1abcf39142aa17b34c4c092386a7abbfe028afdbebc14f2ce26fbee5edeca11502d39a6b7403154843d98a62a7"
-  },
-  // PKCS#1 v1.5 Signature Example 1.12
-  {
-    "message_hex": "bc99a932aa16d622bfff79c50b4c42358673261129e28d6a918ff1b0f1c4f46ad8afa98b0ca0f56f967975b0a29be882e93b6cd3fc33e1faef72e52b2ae0a3f12024506e25690e902e782982145556532284cf505789738f4da31fa1333d3af862b2ba6b6ce7ab4cce6aba",
-    "signature_hex": "872ec5ad4f1846256f17e9936ac50e43e9963ea8c1e76f15879b7874d77d122a609dc8c561145b94bf4ffdffdeb17e6e76ffc6c10c0747f5e37a9f434f5609e79da5250215a457afdf12c6507cc1551f54a28010595826a2c9b97fa0aa851cc68b705d7a06d720ba027e4a1c0b019500fb63b78071684dcfa9772700b982dc66"
-  },
-  // PKCS#1 v1.5 Signature Example 1.13
-  {
-    "message_hex": "731e172ac063992c5b11ba170dfb23bb000d47ba195329cf278061037381514c146064c5285db130dd5bae98b772225950eab05d3ea996f6fffb9a8c8622913f279914c89ada4f3dd77666a868bfcbff2b95b7daf453d4e2c9d75beee7f8e70905e4066a4f73aecc67f956aa5a3292b8488c917d317cfdc86253e690381e15ab",
-    "signature_hex": "76204eacc1d63ec1d6ad5bd0692e1a2f686df6e64ca945c77a824de212efa6d9782d81b4591403ff4020620298c07ebd3a8a61c5bf4dad62cbfc4ae6a03937be4b49a216d570fc6e81872937876e27bd19cf601effc30ddca573c9d56cd4569bdb4851c450c42cb21e738cdd61027b8be5e9b410fc46aa3f29e4be9e64451346"
-  },
-  // PKCS#1 v1.5 Signature Example 1.14
-  {
-    "message_hex": "0211382683a74d8d2a2cb6a06550563be1c26ca62821e4ff163b720464fc3a28d91bedddc62749a5538eaf41fbe0c82a77e06ad99383c9e985ffb8a93fd4d7c58db51ad91ba461d69a8fd7ddabe2496757a0c49122c1a79a85cc0553e8214d036dfe0185efa0d05860c612fa0882c82d246e5830a67355dff18a2c36b732f988cfedc562264c6254b40fcabb97b760947568dcd6a17cda6ee8855bddbab93702471aa0cfb1bed2e13118eba1175b73c96253c108d0b2aba05ab8e17e84392e20085f47404d8365527dc3fb8f2bb48a50038e71361ccf973407",
-    "signature_hex": "525500918331f1042eae0c5c2054aa7f92deb26991b5796634f229daf9b49eb2054d87319f3cfa9b466bd075ef6699aea4bd4a195a1c52968b5e2b75e092d846ea1b5cc27905a8e1d5e5de0edfdb21391ebb951864ebd9f0b0ec35b6542871360a317b7ef13ae06af684e38e21b1e19bc7298e5d6fe0013a164bfa25d3e7313d"
-  },
-  // PKCS#1 v1.5 Signature Example 1.15
-  {
-    "message_hex": "fc6b700d22583388ab2f8dafcaf1a05620698020da4bae44dafbd0877b5012506dc3181d5c66bf023f348b41fd9f94795ab96452a4219f2d39d72af359cf195651c7",
-    "signature_hex": "4452a6cc2626b01e95ab306df0d0cc7484fbab3c22e9703283567f66eadc248dbda58fce7dd0c70cce3f150fca4b369dff3b6237e2b16281ab55b53fb13089c85cd265056b3d62a88bfc2135b16791f7fbcab9fd2dc33becb617be419d2c046142a4d47b338314552edd4b6fe9ce1104ecec4a9958d7331e930fc09bf08a6e64"
-  },
-  // PKCS#1 v1.5 Signature Example 1.16
-  {
-    "message_hex": "13ba086d709cfa5fedaa557a89181a6140f2300ed6d7c3febb6cf68abebcbc678f2bca3dc2330295eec45bb1c4075f3ada987eae88b39c51606cb80429e649d98acc8441b1f8897db86c5a4ce0abf28b1b81dca3667697b850696b74a5ebd85dec56c90f8abe513efa857853720be319607921bca947522cd8fac8cace5b827c3e5a129e7ee57f6b84932f14141ac4274e8cbb46e6912b0d3e2177d499d1840cd47d4d7ae0b4cdc4d3",
-    "signature_hex": "1f3b5a87db72a2c97bb3eff2a65a301268eacd89f42abc1098c1f2de77b0832a65d7815feb35070063f221bb3453bd434386c9a3fde18e3ca1687fb649e86c51d658619dde5debb86fe15491ff77ab748373f1be508880d66ea81e870e91cdf1704875c17f0b10103188bc64eef5a3551b414c733670215b1a22702562581ab1"
-  },
-  // PKCS#1 v1.5 Signature Example 1.17
-  {
-    "message_hex": "eb1e5935",
-    "signature_hex": "370cb9839ae6074f84b2acd6e6f6b7921b4b523463757f6446716140c4e6c0e75bec6ad0197ebfa86bf46d094f5f6cd36dca3a5cc73c8bbb70e2c7c9ab5d964ec8e3dfde481b4a1beffd01b4ad15b31ae7aebb9b70344a9411083165fdf9c3754bbb8b94dd34bd4813dfada1f6937de4267d5597ca09a31e83d7f1a79dd19b5e"
-  },
-  // PKCS#1 v1.5 Signature Example 1.18
-  {
-    "message_hex": "6346b153e889c8228209630071c8a57783f368760b8eb908cfc2b276",
-    "signature_hex": "2479c975c5b1ae4c4e940f473a9045b8bf5b0bfca78ec29a38dfbedc8a749b7a2692f7c52d5bc7c831c7232372a00fed3b6b49e760ec99e074ff2eead5134e8305725dfa39212b84bd4b8d80bc8bc17a512823a3beb18fc08e45ed19c26c817707d67fb05832ef1f12a33e90cd93b8a780319e2963ca25a2af7b09ad8f595c21"
-  },
-  // PKCS#1 v1.5 Signature Example 1.19
-  {
-    "message_hex": "64702db9f825a0f3abc361974659f5e9d30c3aa4f56feac69050c72905e77fe0c22f88a378c21fcf45fe8a5c717302093929",
-    "signature_hex": "152f3451c858d69594e6567dfb31291c1ee7860b9d15ebd5a5edd276ac3e6f7a8d1480e42b3381d2be023acf7ebbdb28de3d2163ae44259c6df98c335d045b61dac9dba9dbbb4e6ab4a083cd76b580cbe472206a1a9fd60680ceea1a570a29b0881c775eaef5525d6d2f344c28837d0aca422bbb0f1aba8f6861ae18bd73fe44"
-  },
-  // PKCS#1 v1.5 Signature Example 1.20
-  {
-    "message_hex": "941921de4a1c9c1618d6f3ca3c179f6e29bae6ddf9a6a564f929e3ce82cf3265d7837d5e692be8dcc9e86c",
-    "signature_hex": "7076c287fc6fff2b20537435e5a3107ce4da10716186d01539413e609d27d1da6fd952c61f4bab91c045fa4f8683ecc4f8dde74227f773cff3d96db84718c4944b06affeba94b725f1b07d3928b2490a85c2f1abf492a9177a7cd2ea0c9668756f825bbec900fa8ac3824e114387ef573780ca334882387b94e5aad7a27a28dc"
-  }
-]
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index e4cc6d8..395b18c 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -915,7 +915,7 @@
   // For the non-root render pass.
   gfx::Rect damage_rect = render_pass->damage_rect;
   if (!frame_buffer_damage.IsEmpty()) {
-    gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+    gfx::Transform inverse_transform;
     if (render_pass->transform_to_root_target.GetInverse(&inverse_transform)) {
       // |frame_buffer_damage| is in the root target space. Transform the damage
       // from the root to the non-root space before it's added.
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 78b1da4..8ebbec16d 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -1268,13 +1268,11 @@
         // positive scale check.
         if (current_sqs_intersects_occlusion &&
             transform.IsPositiveScaleOrTranslation()) {
-          gfx::Transform reverse_transform;
-          bool is_invertible = transform.GetInverse(&reverse_transform);
           // Scale transform can be inverted by multiplying 1/scale (given
           // scale > 0) and translation transform can be inverted by applying
           // the reversed directional translation. Therefore, |transform| is
           // always invertible.
-          DCHECK(is_invertible);
+          gfx::Transform reverse_transform = transform.GetCheckedInverse();
           DCHECK_LE(occlusion_in_target_space.GetRegionComplexity(),
                     settings_.kMaximumOccluderComplexity);
 
diff --git a/components/viz/service/display/draw_polygon.cc b/components/viz/service/display/draw_polygon.cc
index f23be82..26e88723 100644
--- a/components/viz/service/display/draw_polygon.cc
+++ b/components/viz/service/display/draw_polygon.cc
@@ -143,12 +143,7 @@
 // but normal information is no longer needed after sorting.
 void DrawPolygon::ApplyTransformToNormal(const gfx::Transform& transform) {
   // Now we use the inverse transpose of |transform| to transform the normal.
-  gfx::Transform inverse_transform;
-  bool inverted = transform.GetInverse(&inverse_transform);
-  DCHECK(inverted);
-  if (!inverted)
-    return;
-
+  gfx::Transform inverse_transform = transform.GetCheckedInverse();
   inverse_transform.Transpose();
   normal_ = inverse_transform.MapVector(normal_);
 
diff --git a/components/viz/service/display/overlay_candidate_factory.cc b/components/viz/service/display/overlay_candidate_factory.cc
index 28e06f9..96d07db1 100644
--- a/components/viz/service/display/overlay_candidate_factory.cc
+++ b/components/viz/service/display/overlay_candidate_factory.cc
@@ -596,10 +596,10 @@
                                            OverlayCandidate& candidate) const {
   auto& transform = quad->shared_quad_state->quad_to_target_transform;
   auto damage_rect = GetDamageRect(quad, candidate);
-  auto transformed_damage = damage_rect;
-  gfx::Transform inv;
-  if (transform.GetInverse(&inv)) {
-    transformed_damage = inv.MapRect(transformed_damage);
+  gfx::RectF transformed_damage;
+  if (absl::optional<gfx::RectF> transformed =
+          transform.InverseMapRect(damage_rect)) {
+    transformed_damage = *transformed;
     // The quad's |rect| is in content space. To get to buffer space we need
     // to remove the |rect|'s pixel offset.
     auto buffer_damage_origin =
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index dff2041..6907520e5 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -3378,17 +3378,15 @@
     // We cannot handle rotation with clip rect or mask filter.
     DCHECK(
         shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment());
-    quad_to_target_transform_inverse.emplace(
-        gfx::Transform::kSkipInitialization);
+    quad_to_target_transform_inverse.emplace();
     // Flatten before inverting, since we're interested in how points
     // with z=0 in local space map to the clip rect, not in how the clip
     // rect at z=0 in device space maps to some other z in local space.
     gfx::Transform flat_quad_to_target_transform(
         shared_quad_state->quad_to_target_transform);
     flat_quad_to_target_transform.FlattenTo2d();
-    bool result = flat_quad_to_target_transform.GetInverse(
-        &*quad_to_target_transform_inverse);
-    DCHECK(result) << "flat_quad_to_target_transform.GetInverse() failed";
+    quad_to_target_transform_inverse =
+        flat_quad_to_target_transform.GetCheckedInverse();
   }
 
   // The |clip_rect| is in the device coordinate and with all transforms
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 99c275f..7bc56f3 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -116,7 +116,7 @@
     gfx::Rect* quad_space_damage_rect) {
   gfx::Transform quad_to_root_transform =
       target_to_root_transform * quad_to_target_transform;
-  gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+  gfx::Transform inverse_transform;
   bool inverse_valid = quad_to_root_transform.GetInverse(&inverse_transform);
   if (!inverse_valid)
     return false;
@@ -830,7 +830,7 @@
     // might have incompleted copy request, or cached patially drawn render
     // pass.
     if (!RenderPassNeedsFullDamage(resolved_pass)) {
-      gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+      gfx::Transform inverse_transform;
       if (copy_pass->transform_to_root_target.GetInverse(&inverse_transform)) {
         gfx::Rect damage_rect_in_render_pass_space =
             cc::MathUtil::ProjectEnclosingClippedRect(inverse_transform,
@@ -1444,7 +1444,7 @@
     // might have incompleted copy request, or cached patially drawn render
     // pass.
     if (!RenderPassNeedsFullDamage(resolved_pass)) {
-      gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+      gfx::Transform inverse_transform;
       if (copy_pass->transform_to_root_target.GetInverse(&inverse_transform)) {
         gfx::Rect damage_rect_in_render_pass_space =
             cc::MathUtil::ProjectEnclosingClippedRect(inverse_transform,
@@ -1507,8 +1507,7 @@
   // this damage into the local space of the render pass for this purpose.
   gfx::Rect surface_root_rp_damage = resolved_frame.GetSurfaceDamage();
   if (!surface_root_rp_damage.IsEmpty()) {
-    gfx::Transform root_to_target_transform(
-        gfx::Transform::kSkipInitialization);
+    gfx::Transform root_to_target_transform;
     if (target_to_root_transform.GetInverse(&root_to_target_transform)) {
       surface_root_rp_damage = cc::MathUtil::ProjectEnclosingClippedRect(
           root_to_target_transform, surface_root_rp_damage);
@@ -1575,11 +1574,9 @@
           accumulated_damage_in_child_space.Union(damage_from_parent);
           accumulated_damage_in_child_space.Union(surface_root_rp_damage);
           if (!accumulated_damage_in_child_space.IsEmpty()) {
-            gfx::Transform inverse(gfx::Transform::kSkipInitialization);
-            bool inverted =
-                quad->shared_quad_state->quad_to_target_transform.GetInverse(
-                    &inverse);
-            DCHECK(inverted);
+            gfx::Transform inverse =
+                quad->shared_quad_state->quad_to_target_transform
+                    .GetCheckedInverse();
             inverse.PostScale(SK_Scalar1 / x_scale, SK_Scalar1 / y_scale);
             accumulated_damage_in_child_space =
                 cc::MathUtil::ProjectEnclosingClippedRect(
@@ -1829,9 +1826,7 @@
       // transform.
       damage_rect = cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
           root_surface_transform_, damage_rect);
-      gfx::Transform inverse(gfx::Transform::kSkipInitialization);
-      bool inverted = root_surface_transform_.GetInverse(&inverse);
-      DCHECK(inverted);
+      gfx::Transform inverse = root_surface_transform_.GetCheckedInverse();
       damage_rect_surface_space =
           cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(inverse,
                                                                   damage_rect);
diff --git a/components/webcrypto/algorithms/rsa_ssa_unittest.cc b/components/webcrypto/algorithms/rsa_ssa_unittest.cc
index 5946d75ec..26b67055 100644
--- a/components/webcrypto/algorithms/rsa_ssa_unittest.cc
+++ b/components/webcrypto/algorithms/rsa_ssa_unittest.cc
@@ -65,6 +65,12 @@
   d.Set(b, std::move(*va));
 }
 
+std::string FlipHexByte(const std::string& hex, size_t index) {
+  auto bytes = HexStringToBytes(hex);
+  bytes[index] ^= 0xff;
+  return base::HexEncode(base::make_span(bytes));
+}
+
 blink::WebCryptoAlgorithm RS256Algorithm() {
   return CreateRsaHashedImportAlgorithm(
       blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
@@ -105,6 +111,27 @@
                               blink::kWebCryptoKeyUsageSign, &key);
 }
 
+Status ImportSpkiRS256MustFail(const std::string& spki) {
+  auto spki_bytes = HexStringToBytes(spki);
+  blink::WebCryptoKey key;
+  // Note: SPKI keys can only be used for verification, not signing.
+  Status status =
+      ImportKey(blink::kWebCryptoKeyFormatSpki, spki_bytes, RS256Algorithm(),
+                true, blink::kWebCryptoKeyUsageVerify, &key);
+  CHECK(!status.IsSuccess());
+  return status;
+}
+
+Status ImportPkcs8RS256MustFail(const std::string& pkcs8) {
+  auto pkcs8_bytes = HexStringToBytes(pkcs8);
+  blink::WebCryptoKey key;
+  Status status =
+      ImportKey(blink::kWebCryptoKeyFormatPkcs8, pkcs8_bytes, RS256Algorithm(),
+                true, blink::kWebCryptoKeyUsageSign, &key);
+  CHECK(!status.IsSuccess());
+  return status;
+}
+
 std::vector<uint8_t> ExportPkcs8OrDie(blink::WebCryptoKey key) {
   std::vector<uint8_t> exported;
   Status status = ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, &exported);
@@ -673,50 +700,6 @@
   EXPECT_FALSE(is_match);
 }
 
-TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) {
-  // Import the key pair.
-  blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm(
-      blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
-      blink::kWebCryptoAlgorithmIdSha1);
-  blink::WebCryptoKey public_key;
-  blink::WebCryptoKey private_key;
-  ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair(
-      HexStringToBytes(kPublicKeySpkiDerHex),
-      HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false,
-      blink::kWebCryptoKeyUsageVerify, blink::kWebCryptoKeyUsageSign,
-      &public_key, &private_key));
-
-  blink::WebCryptoAlgorithm algorithm =
-      CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5);
-
-  // Validate the signatures are computed and verified as expected.
-  base::Value::List tests = ReadJsonTestFileAsList("pkcs1v15_sign.json");
-
-  std::vector<uint8_t> signature;
-  for (const auto& test_value : tests) {
-    SCOPED_TRACE(&test_value - &tests[0]);
-
-    ASSERT_TRUE(test_value.is_dict());
-    const base::DictionaryValue* test =
-        &base::Value::AsDictionaryValue(test_value);
-
-    std::vector<uint8_t> test_message =
-        GetBytesFromHexString(test, "message_hex");
-    std::vector<uint8_t> test_signature =
-        GetBytesFromHexString(test, "signature_hex");
-
-    signature.clear();
-    ASSERT_EQ(Status::Success(),
-              Sign(algorithm, private_key, test_message, &signature));
-    EXPECT_BYTES_EQ(test_signature, signature);
-
-    bool is_match = false;
-    ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, test_signature,
-                                        test_message, &is_match));
-    EXPECT_TRUE(is_match);
-  }
-}
-
 // Try importing an RSA-SSA public key with unsupported key usages using SPKI
 // format. RSA-SSA public keys only support the 'verify' usage.
 TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_SPKI) {
@@ -1093,32 +1076,105 @@
       StatusToString(ImportJwkRS256MustFail(key)));
 }
 
-// Imports invalid JWK/SPKI/PKCS8 data and verifies that it fails as expected.
-TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) {
-  base::Value::List tests = ReadJsonTestFileAsList("bad_rsa_keys.json");
-  for (const auto& test_value : tests) {
-    SCOPED_TRACE(&test_value - &tests[0]);
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidSpki_Empty) {
+  EXPECT_EQ("DataError", StatusToString(ImportSpkiRS256MustFail("")));
+}
 
-    ASSERT_TRUE(test_value.is_dict());
-    const base::DictionaryValue* test =
-        &base::Value::AsDictionaryValue(test_value);
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidSpki_BadDER) {
+  EXPECT_EQ("DataError", StatusToString(ImportSpkiRS256MustFail("618333c4cb")));
+}
 
-    blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
-    std::vector<uint8_t> key_data =
-        GetKeyDataFromJsonTestCase(test, key_format);
-    std::string test_error;
-    ASSERT_TRUE(test->GetString("error", &test_error));
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidSpki_NotRSA) {
+  EXPECT_EQ("DataError",
+            StatusToString(ImportSpkiRS256MustFail(
+                "3059301306072A8648CE3D020106082A8648CE3D030107034200049CB0CF69"
+                "303DAFC761D4E4687B4ECF039E6D34AB964AF80810D8D558A4A8D6F72D5123"
+                "3A1788920A86EE08A1962C79EFA317FB7879E297DAD2146DB995FA1C78")));
+}
 
-    blink::WebCryptoKeyUsageMask usages = blink::kWebCryptoKeyUsageSign;
-    if (key_format == blink::kWebCryptoKeyFormatSpki)
-      usages = blink::kWebCryptoKeyUsageVerify;
-    blink::WebCryptoKey key;
-    Status status = ImportKey(key_format, key_data,
-                              CreateRsaHashedImportAlgorithm(
-                                  blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
-                                  blink::kWebCryptoAlgorithmIdSha256),
-                              true, usages, &key);
-    EXPECT_EQ(test_error, StatusToString(status));
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidSpki_TrailingData) {
+  EXPECT_EQ(
+      "DataError",
+      StatusToString(ImportSpkiRS256MustFail(
+          "30819F300D06092A864886F70D010101050003818D0030818902818100A56E4A0E70"
+          "1017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB1"
+          "62B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B"
+          "6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1"
+          "386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137020301000100000000")));
+}
+
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidPkcs8_Empty) {
+  EXPECT_EQ("DataError", StatusToString(ImportPkcs8RS256MustFail("")));
+}
+
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidPkcs8_BadDER) {
+  EXPECT_EQ("DataError",
+            StatusToString(ImportPkcs8RS256MustFail("618333c4cb")));
+}
+
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidPkcs8_CRTValuesAreZero) {
+  EXPECT_EQ(
+      "DataError",
+      StatusToString(ImportPkcs8RS256MustFail(
+          "30820138020100300D06092A864886F70D0101010500048201223082011E02010002"
+          "818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD9"
+          "91D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5"
+          "CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2"
+          "D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A21370203010001"
+          "02818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92"
+          "E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE5"
+          "5FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD"
+          "2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B3250201000201"
+          "00020100020100020100")));
+}
+
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidPkcs8_NotRSA) {
+  EXPECT_EQ("DataError",
+            StatusToString(ImportPkcs8RS256MustFail(
+                "308187020100301306072A8648CE3D020106082A8648CE3D030107046D306B"
+                "02010104201FE33950C5F461124AE992C2BDFDF1C73B1615F571BD567E60D1"
+                "9AA1F48CDF42A144034200047C110C66DCFDA807F6E69E45DDB3C74F69A148"
+                "4D203E8DC5ADA8E9A9DD7CB3C70DF448986E51BDE5D1576F99901F9C2C6A80"
+                "6A47FD907643A72B835597EFC8C6")));
+}
+
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidPkcs8_TrailingData) {
+  EXPECT_EQ(
+      "DataError",
+      StatusToString(ImportPkcs8RS256MustFail(
+          "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002"
+          "818100A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD9"
+          "91D8C51056FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5"
+          "CD9508096D5B2B8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2"
+          "D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A21370203010001"
+          "02818033A5042A90B27D4F5451CA9BBBD0B44771A101AF884340AEF9885F2A4BBE92"
+          "E894A724AC3C568C8F97853AD07C0266C8C6A3CA0929F1E8F11231884429FC4D9AE5"
+          "5FEE896A10CE707C3ED7E734E44727A39574501A532683109C2ABACABA283C31B4BD"
+          "2F53C3EE37E352CEE34F9E503BD80C0622AD79C6DCEE883547C6A3B325024100E7E8"
+          "942720A877517273A356053EA2A1BC0C94AA72D55C6E86296B2DFC967948C0A72CBC"
+          "CCA7EACB35706E09A1DF55A1535BD9B3CC34160B3B6DCD3EDA8E6443024100B69DCA"
+          "1CF7D4D7EC81E75B90FCCA874ABCDE123FD2700180AA90479B6E48DE8D67ED24F9F1"
+          "9D85BA275874F542CD20DC723E6963364A1F9425452B269A6799FD024028FA139386"
+          "55BE1F8A159CBACA5A72EA190C30089E19CD274A556F36C4F6E19F554B34C0777904"
+          "27BBDD8DD3EDE2448328F385D81B30E8E43B2FFFA02786197902401A8B38F398FA71"
+          "2049898D7FB79EE0A77668791299CDFA09EFC0E507ACB21ED74301EF5BFD48BE455E"
+          "AEB6E1678255827580A8E4E8E14151D1510A82A3F2E729024027156ABA4126D24A81"
+          "F3A528CBFB27F56886F840A9F6E86E17A44B94FE9319584B8E22FDDE1E5A2E3BD8AA"
+          "5BA8D8584194EB2190ACF832B847F13A3D24A79F4D00000000")));
+}
+
+TEST_F(WebCryptoRsaSsaTest, ImportInvalidPkcs8_CorruptFields) {
+  const struct {
+    size_t byte;
+    const char* name;
+  } kTestCases[] = {
+      {50, "n"},  {168, "e"},  {175, "d"},  {333, "p"},
+      {373, "q"}, {450, "dp"}, {550, "dq"}, {600, "qi"},
+  };
+  for (const auto& test : kTestCases) {
+    std::string key = FlipHexByte(kPrivateKeyPkcs8DerHex, test.byte);
+    EXPECT_EQ("DataError", StatusToString(ImportPkcs8RS256MustFail(key)))
+        << "byte flip should have invalidated key: " << test.name;
   }
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index fe8fe424..d78a9bd 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2802,8 +2802,6 @@
 
   if (use_atk) {
     sources += [
-      "accessibility/accessibility_event_recorder_auralinux.cc",
-      "accessibility/accessibility_event_recorder_auralinux.h",
       "accessibility/browser_accessibility_auralinux.cc",
       "accessibility/browser_accessibility_auralinux.h",
       "accessibility/browser_accessibility_manager_auralinux.cc",
diff --git a/content/browser/accessibility/dump_accessibility_node_browsertest.cc b/content/browser/accessibility/dump_accessibility_node_browsertest.cc
index 3e368115..f89043c 100644
--- a/content/browser/accessibility/dump_accessibility_node_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_node_browsertest.cc
@@ -127,16 +127,6 @@
     base::FilePath accname_file = test_path.Append(base::FilePath(file_path));
     RunTest(accname_file, "accessibility/accname");
   }
-  void RunAccDescTest(const base::FilePath::CharType* file_path) {
-    base::FilePath test_path =
-        GetTestFilePath("accessibility", "accdescription");
-    {
-      base::ScopedAllowBlockingForTesting allow_blocking;
-      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
-    }
-    base::FilePath accname_file = test_path.Append(base::FilePath(file_path));
-    RunTest(accname_file, "accessibility/accdescription");
-  }
 };
 
 class DumpAccessibilityAccNameTestExceptUIA
@@ -321,14 +311,6 @@
 }
 
 //
-// AccDescription tests.
-//
-
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityAccNameTest, DescriptionIgnoresSlot) {
-  RunAccDescTest(FILE_PATH_LITERAL("description-ignores-slot.html"));
-}
-
-//
 // AccName tests.
 //
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityAccNameTest, DescComboboxFocusable) {
@@ -676,10 +658,6 @@
       FILE_PATH_LITERAL("name-heading-combobox-focusable-alternative.html"));
 }
 
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityAccNameTest, NameIgnoresSlot) {
-  RunAccNameTest(FILE_PATH_LITERAL("name-ignores-slot.html"));
-}
-
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityAccNameTest, NameImageCssAfterInLabel) {
   RunAccNameTest(FILE_PATH_LITERAL("name-image-css-after-in-label.html"));
 }
diff --git a/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc b/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
index 37a244d..d89961c 100644
--- a/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
+++ b/content/browser/attribution_reporting/aggregatable_attribution_utils_unittest.cc
@@ -49,35 +49,35 @@
           absl::MakeUint128(/*high=*/0, /*low=*/1024),
           /*source_keys=*/{"key1", "key3"},
           /*filters=*/
-          AttributionFilterData::CreateForTesting({{"filter", {"value"}}}),
-          /*not_filters=*/AttributionFilterData()),
+          *AttributionFilters::Create({{"filter", {"value"}}}),
+          /*not_filters=*/AttributionFilters()),
 
       // The second trigger data applies to "key2", "key4" is ignored.
       AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/2688),
           /*source_keys=*/{"key2", "key4"},
           /*filters=*/
-          AttributionFilterData::CreateForTesting({{"a", {"b", "c"}}}),
-          /*not_filters=*/AttributionFilterData()),
+          *AttributionFilters::Create({{"a", {"b", "c"}}}),
+          /*not_filters=*/AttributionFilters()),
 
       // The third trigger will be ignored due to mismatched filters.
       AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/4096),
           /*source_keys=*/{"key1", "key2"},
           /*filters=*/
-          AttributionFilterData::CreateForTesting({{"filter", {}}}),
-          /*not_filters=*/AttributionFilterData()),
+          *AttributionFilters::Create({{"filter", {}}}),
+          /*not_filters=*/AttributionFilters()),
 
       // The fourth trigger will be ignored due to matched not_filters.
       AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/0, /*low=*/4096),
           /*source_keys=*/{"key1", "key2"},
-          /*filters=*/AttributionFilterData(),
+          /*filters=*/AttributionFilters(),
           /*not_filters=*/
-          AttributionFilterData::CreateForTesting({{"filter", {"value"}}}))};
+          *AttributionFilters::Create({{"filter", {"value"}}}))};
 
   absl::optional<AttributionFilterData> source_filter_data =
-      AttributionFilterData::FromSourceFilterValues({{"filter", {"value"}}});
+      AttributionFilterData::Create({{"filter", {"value"}}});
   ASSERT_TRUE(source_filter_data.has_value());
 
   auto aggregatable_values = AttributionAggregatableValues::CreateForTesting(
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
index 15a383c..994bbd055 100644
--- a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
+++ b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.cc
@@ -17,8 +17,8 @@
 AttributionAggregatableTriggerData::Create(
     absl::uint128 key_piece,
     base::flat_set<std::string> source_keys,
-    AttributionFilterData filters,
-    AttributionFilterData not_filters) {
+    AttributionFilters filters,
+    AttributionFilters not_filters) {
   if (source_keys.size() >
       blink::kMaxAttributionAggregationKeysPerSourceOrTrigger) {
     return absl::nullopt;
@@ -40,8 +40,8 @@
 AttributionAggregatableTriggerData::CreateForTesting(
     absl::uint128 key_piece,
     base::flat_set<std::string> source_keys,
-    AttributionFilterData filters_values,
-    AttributionFilterData not_filters_values) {
+    AttributionFilters filters_values,
+    AttributionFilters not_filters_values) {
   return AttributionAggregatableTriggerData(key_piece, std::move(source_keys),
                                             std::move(filters_values),
                                             std::move(not_filters_values));
@@ -50,8 +50,8 @@
 AttributionAggregatableTriggerData::AttributionAggregatableTriggerData(
     absl::uint128 key_piece,
     base::flat_set<std::string> source_keys,
-    AttributionFilterData filters,
-    AttributionFilterData not_filters)
+    AttributionFilters filters,
+    AttributionFilters not_filters)
     : key_piece_(key_piece),
       source_keys_(std::move(source_keys)),
       filters_(std::move(filters)),
diff --git a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h
index c71a1d41..5e5fc82 100644
--- a/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h
+++ b/content/browser/attribution_reporting/attribution_aggregatable_trigger_data.h
@@ -20,14 +20,14 @@
   static absl::optional<AttributionAggregatableTriggerData> Create(
       absl::uint128 key_piece,
       base::flat_set<std::string> source_keys,
-      AttributionFilterData filters,
-      AttributionFilterData not_filters);
+      AttributionFilters filters,
+      AttributionFilters not_filters);
 
   static AttributionAggregatableTriggerData CreateForTesting(
       absl::uint128 key_piece,
       base::flat_set<std::string> source_keys,
-      AttributionFilterData filters,
-      AttributionFilterData not_filters);
+      AttributionFilters filters,
+      AttributionFilters not_filters);
 
   ~AttributionAggregatableTriggerData();
 
@@ -45,20 +45,20 @@
     return source_keys_;
   }
 
-  const AttributionFilterData& filters() const { return filters_; }
+  const AttributionFilters& filters() const { return filters_; }
 
-  const AttributionFilterData& not_filters() const { return not_filters_; }
+  const AttributionFilters& not_filters() const { return not_filters_; }
 
  private:
   AttributionAggregatableTriggerData(absl::uint128 key_piece,
                                      base::flat_set<std::string> source_keys,
-                                     AttributionFilterData filters,
-                                     AttributionFilterData not_filters);
+                                     AttributionFilters filters,
+                                     AttributionFilters not_filters);
 
   absl::uint128 key_piece_;
   base::flat_set<std::string> source_keys_;
-  AttributionFilterData filters_;
-  AttributionFilterData not_filters_;
+  AttributionFilters filters_;
+  AttributionFilters not_filters_;
 };
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index f45f9fd..c799d66 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -109,15 +109,13 @@
   aggregatable_trigger_data.reserve(mojo.size());
 
   for (auto& aggregatable_trigger : mojo) {
-    absl::optional<AttributionFilterData> filters =
-        AttributionFilterData::FromTriggerFilterValues(
-            std::move(aggregatable_trigger->filters->filter_values));
+    absl::optional<AttributionFilters> filters = AttributionFilters::Create(
+        std::move(aggregatable_trigger->filters->filter_values));
     if (!filters.has_value())
       return absl::nullopt;
 
-    absl::optional<AttributionFilterData> not_filters =
-        AttributionFilterData::FromTriggerFilterValues(
-            std::move(aggregatable_trigger->not_filters->filter_values));
+    absl::optional<AttributionFilters> not_filters = AttributionFilters::Create(
+        std::move(aggregatable_trigger->not_filters->filter_values));
     if (!not_filters.has_value())
       return absl::nullopt;
 
@@ -394,7 +392,7 @@
   // renderer. All of the validation here is also performed renderer-side.
 
   absl::optional<AttributionFilterData> filter_data =
-      AttributionFilterData::FromSourceFilterValues(
+      AttributionFilterData::Create(
           std::move(data->filter_data->filter_values));
   if (!filter_data.has_value()) {
     RecordSourceDataHandleStatus(DataHandleStatus::kInvalidData);
@@ -460,18 +458,16 @@
     context.registration_type = RegistrationType::kTrigger;
   }
 
-  absl::optional<AttributionFilterData> filters =
-      AttributionFilterData::FromTriggerFilterValues(
-          std::move(data->filters->filter_values));
+  absl::optional<AttributionFilters> filters =
+      AttributionFilters::Create(std::move(data->filters->filter_values));
   if (!filters.has_value()) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
     mojo::ReportBadMessage("AttributionDataHost: Invalid top-level filters.");
     return;
   }
 
-  absl::optional<AttributionFilterData> not_filters =
-      AttributionFilterData::FromTriggerFilterValues(
-          std::move(data->not_filters->filter_values));
+  absl::optional<AttributionFilters> not_filters =
+      AttributionFilters::Create(std::move(data->not_filters->filter_values));
   if (!not_filters.has_value()) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
     mojo::ReportBadMessage(
@@ -489,8 +485,8 @@
   event_triggers.reserve(data->event_triggers.size());
 
   for (auto& event_trigger : data->event_triggers) {
-    absl::optional<AttributionFilterData> event_filters =
-        AttributionFilterData::FromTriggerFilterValues(
+    absl::optional<AttributionFilters> event_filters =
+        AttributionFilters::Create(
             std::move(event_trigger->filters->filter_values));
     if (!event_filters.has_value()) {
       RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
@@ -499,8 +495,8 @@
       return;
     }
 
-    absl::optional<AttributionFilterData> not_event_filters =
-        AttributionFilterData::FromTriggerFilterValues(
+    absl::optional<AttributionFilters> not_event_filters =
+        AttributionFilters::Create(
             std::move(event_trigger->not_filters->filter_values));
     if (!not_event_filters.has_value()) {
       RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index d3cb163f..6ee543d 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -487,21 +487,21 @@
       mock_manager_,
       HandleTrigger(AttributionTriggerMatches(AttributionTriggerMatcherConfig(
           destination_origin, reporting_origin,
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"a", {"b"}},
           }),
           Optional(789),
           ElementsAre(EventTriggerDataMatches(EventTriggerDataMatcherConfig(
                           1, 2, Optional(3),
-                          *AttributionFilterData::FromTriggerFilterValues({
+                          *AttributionFilters::Create({
                               {"c", {"d"}},
                           }),
-                          *AttributionFilterData::FromTriggerFilterValues({
+                          *AttributionFilters::Create({
                               {"e", {"f"}},
                           }))),
                       EventTriggerDataMatches(EventTriggerDataMatcherConfig(
-                          4, 5, Eq(absl::nullopt), AttributionFilterData(),
-                          AttributionFilterData()))),
+                          4, 5, Eq(absl::nullopt), AttributionFilters(),
+                          AttributionFilters()))),
           Optional(123)))));
 
   {
@@ -515,7 +515,7 @@
     trigger_data->debug_key = 789;
 
     trigger_data->filters = blink::mojom::AttributionFilterData::New(
-        AttributionFilterData::FilterValues({{"a", {"b"}}}));
+        AttributionFilterValues({{"a", {"b"}}}));
     trigger_data->not_filters = blink::mojom::AttributionFilterData::New();
 
     trigger_data->event_triggers.push_back(blink::mojom::EventTriggerData::New(
@@ -523,10 +523,10 @@
         /*priority=*/2, /*dedup_key=*/3,
         /*filters=*/
         blink::mojom::AttributionFilterData::New(
-            AttributionFilterData::FilterValues({{"c", {"d"}}})),
+            AttributionFilterValues({{"c", {"d"}}})),
         /*not_filters=*/
         blink::mojom::AttributionFilterData::New(
-            AttributionFilterData::FilterValues({{"e", {"f"}}}))));
+            AttributionFilterValues({{"e", {"f"}}}))));
 
     trigger_data->event_triggers.push_back(blink::mojom::EventTriggerData::New(
         /*data=*/4,
diff --git a/content/browser/attribution_reporting/attribution_filter_data.cc b/content/browser/attribution_reporting/attribution_filter_data.cc
index ef8f667..b20cadf9a 100644
--- a/content/browser/attribution_reporting/attribution_filter_data.cc
+++ b/content/browser/attribution_reporting/attribution_filter_data.cc
@@ -23,16 +23,41 @@
 
 using ::attribution_reporting::mojom::SourceRegistrationError;
 
+bool IsValidForSourceOrTrigger(const AttributionFilterValues& filter_values) {
+  if (filter_values.size() > blink::kMaxAttributionFiltersPerSource)
+    return false;
+
+  for (const auto& [filter, values] : filter_values) {
+    if (filter.size() > blink::kMaxBytesPerAttributionFilterString)
+      return false;
+
+    if (values.size() > blink::kMaxValuesPerAttributionFilter)
+      return false;
+
+    for (const auto& value : values) {
+      if (value.size() > blink::kMaxBytesPerAttributionFilterString)
+        return false;
+    }
+  }
+
+  return true;
+}
+
+bool IsValidForSource(const AttributionFilterValues& filter_values) {
+  return !filter_values.contains(AttributionFilterData::kSourceTypeFilterKey) &&
+         IsValidForSourceOrTrigger(filter_values);
+}
+
 }  // namespace
 
 // static
-absl::optional<AttributionFilterData>
-AttributionFilterData::DeserializeSourceFilterData(const std::string& string) {
+absl::optional<AttributionFilterData> AttributionFilterData::Deserialize(
+    const std::string& string) {
   proto::AttributionFilterData msg;
   if (!msg.ParseFromString(string))
     return absl::nullopt;
 
-  FilterValues::container_type filter_values;
+  AttributionFilterValues::container_type filter_values;
   filter_values.reserve(msg.filter_values().size());
 
   for (google::protobuf::MapPair<std::string, proto::AttributionFilterValues>&
@@ -51,30 +76,21 @@
                                  std::make_move_iterator(values->end())));
   }
 
-  return FromFilterValues(std::move(filter_values));
+  return Create(std::move(filter_values));
 }
 
 // static
-absl::optional<AttributionFilterData>
-AttributionFilterData::FromSourceFilterValues(FilterValues&& filter_values) {
-  absl::optional<AttributionFilterData> result =
-      FromFilterValues(std::move(filter_values));
-
-  if (!result || result->filter_values_.contains(kSourceTypeFilterKey))
+absl::optional<AttributionFilterData> AttributionFilterData::Create(
+    AttributionFilterValues filter_values) {
+  if (!IsValidForSource(filter_values))
     return absl::nullopt;
 
-  return result;
-}
-
-// static
-absl::optional<AttributionFilterData>
-AttributionFilterData::FromTriggerFilterValues(FilterValues&& filter_values) {
-  return FromFilterValues(std::move(filter_values));
+  return AttributionFilterData(std::move(filter_values));
 }
 
 // static
 base::expected<AttributionFilterData, SourceRegistrationError>
-AttributionFilterData::FromSourceJSON(base::Value* input_value) {
+AttributionFilterData::FromJSON(base::Value* input_value) {
   // TODO(johnidel): Consider logging registration JSON metrics here.
   if (!input_value)
     return AttributionFilterData();
@@ -92,7 +108,7 @@
         SourceRegistrationError::kFilterDataHasSourceTypeKey);
   }
 
-  FilterValues::container_type filter_values;
+  AttributionFilterValues::container_type filter_values;
   filter_values.reserve(dict->size());
 
   for (auto [filter, value] : *dict) {
@@ -131,55 +147,31 @@
   }
 
   return AttributionFilterData(
-      FilterValues(base::sorted_unique, std::move(filter_values)));
+      AttributionFilterValues(base::sorted_unique, std::move(filter_values)));
 }
 
 // static
-AttributionFilterData AttributionFilterData::ForSourceType(
+AttributionFilters AttributionFilters::ForSourceType(
     AttributionSourceType source_type) {
   std::vector<std::string> values;
   values.reserve(1);
   values.push_back(AttributionSourceTypeToString(source_type));
 
-  AttributionFilterData::FilterValues filter_values;
+  AttributionFilterValues filter_values;
   filter_values.reserve(1);
-  filter_values.emplace(kSourceTypeFilterKey, std::move(values));
+  filter_values.emplace(AttributionFilterData::kSourceTypeFilterKey,
+                        std::move(values));
 
-  return AttributionFilterData(std::move(filter_values));
-}
-
-// static
-absl::optional<AttributionFilterData> AttributionFilterData::FromFilterValues(
-    FilterValues&& filter_values) {
-  if (filter_values.size() > blink::kMaxAttributionFiltersPerSource)
-    return absl::nullopt;
-
-  for (const auto& [filter, values] : filter_values) {
-    if (filter.size() > blink::kMaxBytesPerAttributionFilterString)
-      return absl::nullopt;
-
-    if (values.size() > blink::kMaxValuesPerAttributionFilter)
-      return absl::nullopt;
-
-    for (const auto& value : values) {
-      if (value.size() > blink::kMaxBytesPerAttributionFilterString)
-        return absl::nullopt;
-    }
-  }
-
-  return AttributionFilterData(std::move(filter_values));
-}
-
-// static
-AttributionFilterData AttributionFilterData::CreateForTesting(
-    FilterValues filter_values) {
-  return AttributionFilterData(std::move(filter_values));
+  return AttributionFilters(std::move(filter_values));
 }
 
 AttributionFilterData::AttributionFilterData() = default;
 
-AttributionFilterData::AttributionFilterData(FilterValues filter_values)
-    : filter_values_(std::move(filter_values)) {}
+AttributionFilterData::AttributionFilterData(
+    AttributionFilterValues filter_values)
+    : filter_values_(std::move(filter_values)) {
+  DCHECK(IsValidForSource(filter_values_));
+}
 
 AttributionFilterData::~AttributionFilterData() = default;
 
@@ -195,8 +187,6 @@
     AttributionFilterData&&) = default;
 
 std::string AttributionFilterData::Serialize() const {
-  DCHECK(!filter_values_.contains(kSourceTypeFilterKey));
-
   proto::AttributionFilterData msg;
 
   for (const auto& [filter, values] : filter_values_) {
@@ -214,4 +204,32 @@
   return string;
 }
 
+// static
+absl::optional<AttributionFilters> AttributionFilters::Create(
+    AttributionFilterValues filter_values) {
+  if (!IsValidForSourceOrTrigger(filter_values))
+    return absl::nullopt;
+
+  return AttributionFilters(std::move(filter_values));
+}
+
+AttributionFilters::AttributionFilters() = default;
+
+AttributionFilters::AttributionFilters(AttributionFilterValues filter_values)
+    : filter_values_(std::move(filter_values)) {
+  DCHECK(IsValidForSourceOrTrigger(filter_values_));
+}
+
+AttributionFilters::~AttributionFilters() = default;
+
+AttributionFilters::AttributionFilters(const AttributionFilters&) = default;
+
+AttributionFilters::AttributionFilters(AttributionFilters&&) = default;
+
+AttributionFilters& AttributionFilters::operator=(const AttributionFilters&) =
+    default;
+
+AttributionFilters& AttributionFilters::operator=(AttributionFilters&&) =
+    default;
+
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_filter_data.h b/content/browser/attribution_reporting/attribution_filter_data.h
index 6df22f9..0c80d09 100644
--- a/content/browser/attribution_reporting/attribution_filter_data.h
+++ b/content/browser/attribution_reporting/attribution_filter_data.h
@@ -21,34 +21,23 @@
 
 namespace content {
 
+using AttributionFilterValues =
+    base::flat_map<std::string, std::vector<std::string>>;
+
+// Set on sources.
 // Supports persistence to disk via serializaton to/from proto.
 class CONTENT_EXPORT AttributionFilterData {
  public:
   static constexpr char kSourceTypeFilterKey[] = "source_type";
 
-  using FilterValues = base::flat_map<std::string, std::vector<std::string>>;
+  static absl::optional<AttributionFilterData> Deserialize(const std::string&);
 
-  // Deserializes `string`, if valid. Returns `absl::nullopt` if not.
-  static absl::optional<AttributionFilterData> DeserializeSourceFilterData(
-      const std::string& string);
-
-  // Source filter data is not allowed to contain a `source_type` filter.
-  static absl::optional<AttributionFilterData> FromSourceFilterValues(
-      FilterValues&& filter_values);
-
-  // Trigger filter data is allowed to contain a `source_type` filter.
-  static absl::optional<AttributionFilterData> FromTriggerFilterValues(
-      FilterValues&& filter_values);
+  // Filter data is not allowed to contain a `source_type` filter.
+  static absl::optional<AttributionFilterData> Create(AttributionFilterValues);
 
   static base::expected<AttributionFilterData,
                         attribution_reporting::mojom::SourceRegistrationError>
-  FromSourceJSON(base::Value* input_value);
-
-  // Returns filter data that matches only the given source type.
-  static AttributionFilterData ForSourceType(AttributionSourceType source_type);
-
-  // Creates without validation.
-  static AttributionFilterData CreateForTesting(FilterValues filter_values);
+  FromJSON(base::Value*);
 
   AttributionFilterData();
 
@@ -60,17 +49,45 @@
   AttributionFilterData& operator=(const AttributionFilterData&);
   AttributionFilterData& operator=(AttributionFilterData&&);
 
-  const FilterValues& filter_values() const { return filter_values_; }
+  const AttributionFilterValues& filter_values() const {
+    return filter_values_;
+  }
 
   std::string Serialize() const;
 
  private:
-  static absl::optional<AttributionFilterData> FromFilterValues(
-      FilterValues&& filter_values);
+  explicit AttributionFilterData(AttributionFilterValues);
 
-  explicit AttributionFilterData(FilterValues filter_values);
+  AttributionFilterValues filter_values_;
+};
 
-  FilterValues filter_values_;
+// Set on triggers.
+class CONTENT_EXPORT AttributionFilters {
+ public:
+  // Filters are allowed to contain a `source_type` filter.
+  static absl::optional<AttributionFilters> Create(AttributionFilterValues);
+
+  // Returns filters that match only the given source type.
+  static AttributionFilters ForSourceType(AttributionSourceType);
+
+  AttributionFilters();
+
+  ~AttributionFilters();
+
+  AttributionFilters(const AttributionFilters&);
+  AttributionFilters(AttributionFilters&&);
+
+  AttributionFilters& operator=(const AttributionFilters&);
+  AttributionFilters& operator=(AttributionFilters&&);
+
+  const AttributionFilterValues& filter_values() const {
+    return filter_values_;
+  }
+
+ private:
+  explicit AttributionFilters(AttributionFilterValues);
+
+  AttributionFilterValues filter_values_;
 };
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_filter_data_unittest.cc b/content/browser/attribution_reporting/attribution_filter_data_unittest.cc
index e305de4..43d8d2d5 100644
--- a/content/browser/attribution_reporting/attribution_filter_data_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_filter_data_unittest.cc
@@ -26,8 +26,7 @@
 using ::testing::ElementsAre;
 using ::testing::Pair;
 
-std::string CreateSerialized(
-    const AttributionFilterData::FilterValues& filter_values) {
+std::string CreateSerialized(const AttributionFilterValues& filter_values) {
   proto::AttributionFilterData msg;
 
   for (const auto& [filter, values] : filter_values) {
@@ -44,8 +43,8 @@
   return string;
 }
 
-AttributionFilterData::FilterValues CreateFilterValues(size_t n) {
-  AttributionFilterData::FilterValues filter_values;
+AttributionFilterValues CreateFilterValues(size_t n) {
+  AttributionFilterValues filter_values;
   for (size_t i = 0; i < n; i++) {
     filter_values.emplace(base::NumberToString(i), std::vector<std::string>());
   }
@@ -55,61 +54,55 @@
 
 // Tests that a "source_type" filter present in the serialized data is
 // removed.
-TEST(AttributionFilterDataTest,
-     DeserializeSourceFilterData_RemovesSourceTypeFilter) {
+TEST(AttributionFilterDataTest, Deserialize_RemovesSourceTypeFilter) {
   const std::string serialized =
       CreateSerialized({{"source_type", {"abc"}}, {"x", {"y"}}});
 
-  EXPECT_THAT(AttributionFilterData::DeserializeSourceFilterData(serialized)
-                  ->filter_values(),
+  EXPECT_THAT(AttributionFilterData::Deserialize(serialized)->filter_values(),
               ElementsAre(Pair("x", ElementsAre("y"))));
 }
 
 // Tests that serialized data is allowed
 // `blink::kMaxAttributionFiltersPerSource` filters.
-TEST(AttributionFilterDataTest,
-     DeserializeSourceFilterData_AllowsOneExtraFilter) {
-  EXPECT_TRUE(AttributionFilterData::DeserializeSourceFilterData(
+TEST(AttributionFilterDataTest, Deserialize_AllowsOneExtraFilter) {
+  EXPECT_TRUE(AttributionFilterData::Deserialize(
                   CreateSerialized(CreateFilterValues(
                       blink::kMaxAttributionFiltersPerSource)))
                   .has_value());
 
-  EXPECT_FALSE(AttributionFilterData::DeserializeSourceFilterData(
+  EXPECT_FALSE(AttributionFilterData::Deserialize(
                    CreateSerialized(CreateFilterValues(
                        blink::kMaxAttributionFiltersPerSource + 1)))
                    .has_value());
 }
 
-TEST(AttributionFilterDataTest,
-     FromSourceFilterValues_ProhibitsSourceTypeFilter) {
-  EXPECT_FALSE(AttributionFilterData::FromSourceFilterValues(
-      {{"source_type", {"event"}}}));
+TEST(AttributionFilterDataTest, Create_ProhibitsSourceTypeFilter) {
+  EXPECT_FALSE(AttributionFilterData::Create({{"source_type", {"event"}}}));
 }
 
 TEST(AttributionFilterDataTest,
      FromTriggerFilterValues_AllowsSourceTypeFilter) {
-  EXPECT_TRUE(AttributionFilterData::FromTriggerFilterValues(
-      {{"source_type", {"event"}}}));
+  EXPECT_TRUE(AttributionFilters::Create({{"source_type", {"event"}}}));
 }
 
-TEST(AttributionFilterDatTest, FromSourceFilterValues_LimitsFilterCount) {
-  EXPECT_TRUE(AttributionFilterData::FromSourceFilterValues(
+TEST(AttributionFilterDatTest, Create_LimitsFilterCount) {
+  EXPECT_TRUE(AttributionFilterData::Create(
                   CreateFilterValues(blink::kMaxAttributionFiltersPerSource))
                   .has_value());
 
   EXPECT_FALSE(
-      AttributionFilterData::FromSourceFilterValues(
+      AttributionFilterData::Create(
           CreateFilterValues(blink::kMaxAttributionFiltersPerSource + 1))
           .has_value());
 }
 
 TEST(AttributionFilterDatTest, FromTriggerFilterValues_LimitsFilterCount) {
-  EXPECT_TRUE(AttributionFilterData::FromTriggerFilterValues(
+  EXPECT_TRUE(AttributionFilters::Create(
                   CreateFilterValues(blink::kMaxAttributionFiltersPerSource))
                   .has_value());
 
   EXPECT_FALSE(
-      AttributionFilterData::FromTriggerFilterValues(
+      AttributionFilters::Create(
           CreateFilterValues(blink::kMaxAttributionFiltersPerSource + 1))
           .has_value());
 }
diff --git a/content/browser/attribution_reporting/attribution_header_utils.cc b/content/browser/attribution_reporting/attribution_header_utils.cc
index 09ba6944..6798dfe 100644
--- a/content/browser/attribution_reporting/attribution_header_utils.cc
+++ b/content/browser/attribution_reporting/attribution_header_utils.cc
@@ -93,7 +93,7 @@
   absl::optional<uint64_t> debug_key = ParseDebugKey(registration);
 
   base::expected<AttributionFilterData, SourceRegistrationError> filter_data =
-      AttributionFilterData::FromSourceJSON(registration.Find("filter_data"));
+      AttributionFilterData::FromJSON(registration.Find("filter_data"));
   if (!filter_data.has_value())
     return base::unexpected(filter_data.error());
 
diff --git a/content/browser/attribution_reporting/attribution_header_utils_unittest.cc b/content/browser/attribution_reporting/attribution_header_utils_unittest.cc
index aff3a386..a9feebbd 100644
--- a/content/browser/attribution_reporting/attribution_header_utils_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_header_utils_unittest.cc
@@ -189,7 +189,7 @@
             "c": ["e", "d"],
             "f": []
           })json"),
-          AttributionFilterData::CreateForTesting({
+          *AttributionFilterData::Create({
               {"a", {"b"}},
               {"c", {"e", "d"}},
               {"f", {}},
@@ -241,30 +241,30 @@
   };
 
   for (auto& test_case : kTestCases) {
-    EXPECT_EQ(AttributionFilterData::FromSourceJSON(
-                  base::OptionalToPtr(test_case.json)),
-              test_case.expected)
+    EXPECT_EQ(
+        AttributionFilterData::FromJSON(base::OptionalToPtr(test_case.json)),
+        test_case.expected)
         << test_case.description;
   }
 
   {
     base::Value json = make_filter_data_with_keys(50);
-    EXPECT_TRUE(AttributionFilterData::FromSourceJSON(&json).has_value());
+    EXPECT_TRUE(AttributionFilterData::FromJSON(&json).has_value());
   }
 
   {
     base::Value json = make_filter_data_with_key_length(25);
-    EXPECT_TRUE(AttributionFilterData::FromSourceJSON(&json).has_value());
+    EXPECT_TRUE(AttributionFilterData::FromJSON(&json).has_value());
   }
 
   {
     base::Value json = make_filter_data_with_values(50);
-    EXPECT_TRUE(AttributionFilterData::FromSourceJSON(&json).has_value());
+    EXPECT_TRUE(AttributionFilterData::FromJSON(&json).has_value());
   }
 
   {
     base::Value json = make_filter_data_with_value_length(25);
-    EXPECT_TRUE(AttributionFilterData::FromSourceJSON(&json).has_value());
+    EXPECT_TRUE(AttributionFilterData::FromJSON(&json).has_value());
   }
 }
 
@@ -490,7 +490,7 @@
                   reporting_origin, source_time, default_expiry_time,
                   source_type,
                   /*priority=*/0,
-                  AttributionFilterData::CreateForTesting({{"a", {"b"}}}),
+                  *AttributionFilterData::Create({{"a", {"b"}}}),
                   /*debug_key=*/absl::nullopt, AttributionAggregationKeys()),
               /*is_within_fenced_frame=*/false,
               /*debug_reporting=*/false),
diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
index 9d1d917..a8cf8f1 100644
--- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
@@ -271,8 +271,8 @@
                .SetPriority(std::numeric_limits<int64_t>::max())
                .SetDedupKeys({13, 17})
                .SetAggregatableBudgetConsumed(1300)
-               .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
-                   {{"a", {"b", "c"}}}))
+               .SetFilterData(
+                   *AttributionFilterData::Create({{"a", {"b", "c"}}}))
                .SetAggregationKeys(
                    *AttributionAggregationKeys::FromKeys({{"a", 1}}))
                .SetAggregatableDedupKeys({14, 18})
@@ -1011,8 +1011,8 @@
   const AttributionTrigger trigger(
       url::Origin::Create(GURL("https://d.test")),
       url::Origin::Create(GURL("https://r.test")),
-      /*filters=*/AttributionFilterData::CreateForTesting({{"a", {"b"}}}),
-      /*not_filters=*/AttributionFilterData::CreateForTesting({{"g", {"h"}}}),
+      /*filters=*/*AttributionFilters::Create({{"a", {"b"}}}),
+      /*not_filters=*/*AttributionFilters::Create({{"g", {"h"}}}),
       /*debug_key=*/1,
       /*aggregatable_dedup_key=*/18,
       {
@@ -1021,28 +1021,28 @@
               /*priority=*/3,
               /*dedup_key=*/absl::nullopt,
               /*filters=*/
-              AttributionFilterData::CreateForTesting({{"c", {"d"}}}),
-              /*not_filters=*/AttributionFilterData()),
+              *AttributionFilters::Create({{"c", {"d"}}}),
+              /*not_filters=*/AttributionFilters()),
           AttributionTrigger::EventTriggerData(
               /*data=*/4,
               /*priority=*/5,
               /*dedup_key=*/6,
-              /*filters=*/AttributionFilterData(),
+              /*filters=*/AttributionFilters(),
               /*not_filters=*/
-              AttributionFilterData::CreateForTesting({{"e", {"f"}}})),
+              *AttributionFilters::Create({{"e", {"f"}}})),
       },
       {AttributionAggregatableTriggerData::CreateForTesting(
            /*key_piece=*/345,
            /*source_keys=*/{"a"},
            /*filters=*/
-           AttributionFilterData::CreateForTesting({{"c", {"d"}}}),
-           /*not_filters=*/AttributionFilterData()),
+           *AttributionFilters::Create({{"c", {"d"}}}),
+           /*not_filters=*/AttributionFilters()),
        AttributionAggregatableTriggerData::CreateForTesting(
            /*key_piece=*/678,
            /*source_keys=*/{"b"},
-           /*filters=*/AttributionFilterData(),
+           /*filters=*/AttributionFilters(),
            /*not_filters=*/
-           AttributionFilterData::CreateForTesting({{"e", {"f"}}}))},
+           *AttributionFilters::Create({{"e", {"f"}}}))},
       /*aggregatable_values=*/
       AttributionAggregatableValues::CreateForTesting(
           {{"a", 123}, {"b", 456}}));
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 7e57cb2c..35b914df 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -334,8 +334,7 @@
   }
 
   absl::optional<AttributionFilterData> filter_data =
-      AttributionFilterData::DeserializeSourceFilterData(
-          statement.ColumnString(col++));
+      AttributionFilterData::Deserialize(statement.ColumnString(col++));
   if (!filter_data)
     return absl::nullopt;
 
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index 37689f8..4d75502 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -2494,8 +2494,7 @@
                              .SetSourceType(AttributionSourceType::kNavigation)
                              .Build());
 
-  auto filter_data =
-      AttributionFilterData::FromSourceFilterValues({{"abc", {"x", "y"}}});
+  const auto filter_data = AttributionFilterData::Create({{"abc", {"x", "y"}}});
   ASSERT_TRUE(filter_data.has_value());
 
   storage()->StoreSource(SourceBuilder()
@@ -2503,12 +2502,9 @@
                              .SetSourceType(AttributionSourceType::kEvent)
                              .Build());
 
-  EXPECT_THAT(
-      storage()->GetActiveSources(),
-      ElementsAre(SourceFilterDataIs(AttributionFilterData()),
-                  SourceFilterDataIs(AttributionFilterData::CreateForTesting({
-                      {"abc", {"x", "y"}},
-                  }))));
+  EXPECT_THAT(storage()->GetActiveSources(),
+              ElementsAre(SourceFilterDataIs(AttributionFilterData()),
+                          SourceFilterDataIs(filter_data)));
 }
 
 TEST_F(AttributionStorageTest, NoMatchingTriggerData_ReturnsError) {
@@ -2520,23 +2516,23 @@
                              .SetReportingOrigin(origin)
                              .Build());
 
-  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingConfigurations,
-            MaybeCreateAndStoreEventLevelReport(AttributionTrigger(
-                origin, origin,
-                /*filters=*/AttributionFilterData(),
-                /*not_filters=*/AttributionFilterData(),
-                /*debug_key=*/absl::nullopt,
-                /*aggregatable_dedup_key=*/absl::nullopt,
-                {AttributionTrigger::EventTriggerData(
-                    /*data=*/11,
-                    /*priority=*/12,
-                    /*dedup_key=*/13,
-                    /*filters=*/
-                    AttributionFilterData::ForSourceType(
-                        AttributionSourceType::kEvent),
-                    /*not_filters=*/AttributionFilterData())},
-                /*aggregatable_trigger_data=*/{},
-                /*aggregatable_values=*/AttributionAggregatableValues())));
+  EXPECT_EQ(
+      AttributionTrigger::EventLevelResult::kNoMatchingConfigurations,
+      MaybeCreateAndStoreEventLevelReport(AttributionTrigger(
+          origin, origin,
+          /*filters=*/AttributionFilters(),
+          /*not_filters=*/AttributionFilters(),
+          /*debug_key=*/absl::nullopt,
+          /*aggregatable_dedup_key=*/absl::nullopt,
+          {AttributionTrigger::EventTriggerData(
+              /*data=*/11,
+              /*priority=*/12,
+              /*dedup_key=*/13,
+              /*filters=*/
+              AttributionFilters::ForSourceType(AttributionSourceType::kEvent),
+              /*not_filters=*/AttributionFilters())},
+          /*aggregatable_trigger_data=*/{},
+          /*aggregatable_values=*/AttributionAggregatableValues())));
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()), IsEmpty());
 
@@ -2552,8 +2548,7 @@
           .SetSourceType(AttributionSourceType::kNavigation)
           .SetDestinationOrigin(origin)
           .SetReportingOrigin(origin)
-          .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
-              {{"abc", {"123"}}}))
+          .SetFilterData(*AttributionFilterData::Create({{"abc", {"123"}}}))
           .Build());
 
   const std::vector<AttributionTrigger::EventTriggerData> event_triggers = {
@@ -2563,10 +2558,10 @@
           /*priority=*/12,
           /*dedup_key=*/13,
           /*filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"abc", {"456"}},
           }),
-          /*not_filters=*/AttributionFilterData()),
+          /*not_filters=*/AttributionFilters()),
 
       // Filters match, but negated filters do not.
       AttributionTrigger::EventTriggerData(
@@ -2574,11 +2569,11 @@
           /*priority=*/22,
           /*dedup_key=*/23,
           /*filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"abc", {"123"}},
           }),
           /*not_filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"source_type", {"navigation"}},
           })),
 
@@ -2588,11 +2583,11 @@
           /*priority=*/32,
           /*dedup_key=*/33,
           /*filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"abc", {"123"}},
           }),
           /*not_filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"source_type", {"event"}},
           })),
 
@@ -2603,11 +2598,11 @@
           /*priority=*/42,
           /*dedup_key=*/43,
           /*filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"abc", {"123"}},
           }),
           /*not_filters=*/
-          *AttributionFilterData::FromTriggerFilterValues({
+          *AttributionFilters::Create({
               {"source_type", {"event"}},
           })),
   };
@@ -2615,8 +2610,8 @@
   EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
             MaybeCreateAndStoreEventLevelReport(AttributionTrigger(
                 origin, origin,
-                /*filters=*/AttributionFilterData(),
-                /*not_filters=*/AttributionFilterData(),
+                /*filters=*/AttributionFilters(),
+                /*not_filters=*/AttributionFilters(),
                 /*debug_key=*/absl::nullopt,
                 /*aggregatable_dedup_key=*/absl::nullopt, event_triggers,
                 /*aggregatable_trigger_data=*/{},
@@ -2637,8 +2632,8 @@
       AttributionAggregatableTriggerData::CreateForTesting(
           absl::MakeUint128(/*high=*/1, /*low=*/0),
           /*source_keys=*/{"0"},
-          /*filters=*/AttributionFilterData(),
-          /*not_filters=*/AttributionFilterData())};
+          /*filters=*/AttributionFilters(),
+          /*not_filters=*/AttributionFilters())};
 
   auto aggregatable_values =
       AttributionAggregatableValues::CreateForTesting({{"0", 1}});
@@ -2647,17 +2642,16 @@
       SourceBuilder()
           .SetDestinationOrigin(origin)
           .SetReportingOrigin(origin)
-          .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
-              {{"abc", {"123"}}}))
+          .SetFilterData(*AttributionFilterData::Create({{"abc", {"123"}}}))
           .SetAggregationKeys(*AttributionAggregationKeys::FromKeys({{"0", 1}}))
           .Build());
 
   AttributionTrigger trigger1(origin, origin,
                               /*filters=*/
-                              *AttributionFilterData::FromTriggerFilterValues({
+                              *AttributionFilters::Create({
                                   {"abc", {"456"}},
                               }),
-                              /*not_filters=*/AttributionFilterData(),
+                              /*not_filters=*/AttributionFilters(),
                               /*debug_key=*/absl::nullopt,
                               /*aggregatable_dedup_key=*/absl::nullopt,
                               /*event_triggers=*/{}, aggregatable_trigger_data,
@@ -2665,10 +2659,10 @@
 
   AttributionTrigger trigger2(origin, origin,
                               /*filters=*/
-                              *AttributionFilterData::FromTriggerFilterValues({
+                              *AttributionFilters::Create({
                                   {"abc", {"123"}},
                               }),
-                              /*not_filters=*/AttributionFilterData(),
+                              /*not_filters=*/AttributionFilters(),
                               /*debug_key=*/absl::nullopt,
                               /*aggregatable_dedup_key=*/absl::nullopt,
                               /*event_triggers=*/{}, aggregatable_trigger_data,
@@ -2676,9 +2670,9 @@
 
   AttributionTrigger trigger3(
       origin, origin,
-      /*filters=*/AttributionFilterData(),
+      /*filters=*/AttributionFilters(),
       /*not_filters=*/
-      AttributionFilterData::ForSourceType(AttributionSourceType::kNavigation),
+      AttributionFilters::ForSourceType(AttributionSourceType::kNavigation),
       /*debug_key=*/absl::nullopt,
       /*aggregatable_dedup_key=*/absl::nullopt,
       /*event_triggers=*/{}, aggregatable_trigger_data, aggregatable_values);
@@ -2815,8 +2809,7 @@
 TEST_F(AttributionStorageTest, AggregatableReportFiltering) {
   storage()->StoreSource(
       SourceBuilder()
-          .SetFilterData(*AttributionFilterData::FromSourceFilterValues(
-              {{"abc", {"123"}}}))
+          .SetFilterData(*AttributionFilterData::Create({{"abc", {"123"}}}))
           .SetAggregationKeys(*AttributionAggregationKeys::FromKeys({{"0", 1}}))
           .Build());
 
@@ -2827,8 +2820,8 @@
                             absl::MakeUint128(/*high=*/1, /*low=*/0),
                             /*source_keys=*/{"0"},
                             /*filters=*/
-                            AttributionFilterData(),
-                            /*not_filters=*/AttributionFilterData())})
+                            AttributionFilters(),
+                            /*not_filters=*/AttributionFilters())})
                     .Build()),
             AttributionTrigger::AggregatableResult::kNoHistograms);
 }
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index fc155ba..87687126 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -656,22 +656,22 @@
   std::vector<AttributionTrigger::EventTriggerData> event_triggers;
 
   if (generate_event_trigger_data) {
-    event_triggers.emplace_back(trigger_data_, priority_, dedup_key_,
-                                /*filters=*/
-                                AttributionFilterData::ForSourceType(
-                                    AttributionSourceType::kNavigation),
-                                /*not_filters=*/AttributionFilterData());
+    event_triggers.emplace_back(
+        trigger_data_, priority_, dedup_key_,
+        /*filters=*/
+        AttributionFilters::ForSourceType(AttributionSourceType::kNavigation),
+        /*not_filters=*/AttributionFilters());
 
     event_triggers.emplace_back(
         event_source_trigger_data_, priority_, dedup_key_,
         /*filters=*/
-        AttributionFilterData::ForSourceType(AttributionSourceType::kEvent),
-        /*not_filters=*/AttributionFilterData());
+        AttributionFilters::ForSourceType(AttributionSourceType::kEvent),
+        /*not_filters=*/AttributionFilters());
   }
 
   return AttributionTrigger(destination_origin_, reporting_origin_,
-                            /*filters=*/AttributionFilterData(),
-                            /*not_filters=*/AttributionFilterData(), debug_key_,
+                            /*filters=*/AttributionFilters(),
+                            /*not_filters=*/AttributionFilters(), debug_key_,
                             aggregatable_dedup_key_, std::move(event_triggers),
                             aggregatable_trigger_data_, aggregatable_values_);
 }
@@ -787,6 +787,10 @@
   return a.filter_values() == b.filter_values();
 }
 
+bool operator==(const AttributionFilters& a, const AttributionFilters& b) {
+  return a.filter_values() == b.filter_values();
+}
+
 bool operator==(const CommonSourceInfo& a, const CommonSourceInfo& b) {
   const auto tie = [](const CommonSourceInfo& source) {
     return std::make_tuple(source.source_event_id(), source.source_origin(),
@@ -1096,12 +1100,15 @@
   return out << "}";
 }
 
-std::ostream& operator<<(std::ostream& out,
-                         const AttributionFilterData& filter_data) {
+namespace {
+
+std::ostream& WriteAttributionFilterValues(
+    std::ostream& out,
+    const AttributionFilterValues& filter_values) {
   out << "{";
 
   const char* outer_separator = "";
-  for (const auto& [filter, values] : filter_data.filter_values()) {
+  for (const auto& [filter, values] : filter_values) {
     out << outer_separator << filter << "=[";
 
     const char* inner_separator = "";
@@ -1117,6 +1124,17 @@
   return out << "}";
 }
 
+}  // namespace
+
+std::ostream& operator<<(std::ostream& out,
+                         const AttributionFilterData& filter_data) {
+  return WriteAttributionFilterValues(out, filter_data.filter_values());
+}
+
+std::ostream& operator<<(std::ostream& out, const AttributionFilters& filters) {
+  return WriteAttributionFilterValues(out, filters.filter_values());
+}
+
 std::ostream& operator<<(std::ostream& out, const CommonSourceInfo& source) {
   return out << "{source_event_id=" << source.source_event_id()
              << ",source_origin=" << source.source_origin()
@@ -1348,8 +1366,8 @@
     ::testing::Matcher<uint64_t> data,
     ::testing::Matcher<int64_t> priority,
     ::testing::Matcher<absl::optional<uint64_t>> dedup_key,
-    ::testing::Matcher<const AttributionFilterData&> filters,
-    ::testing::Matcher<const AttributionFilterData&> not_filters)
+    ::testing::Matcher<const AttributionFilters&> filters,
+    ::testing::Matcher<const AttributionFilters&> not_filters)
     : data(std::move(data)),
       priority(std::move(priority)),
       dedup_key(std::move(dedup_key)),
@@ -1375,7 +1393,7 @@
 AttributionTriggerMatcherConfig::AttributionTriggerMatcherConfig(
     ::testing::Matcher<const url::Origin&> destination_origin,
     ::testing::Matcher<const url::Origin&> reporting_origin,
-    ::testing::Matcher<const AttributionFilterData&> filters,
+    ::testing::Matcher<const AttributionFilters&> filters,
     ::testing::Matcher<absl::optional<uint64_t>> debug_key,
     ::testing::Matcher<const std::vector<AttributionTrigger::EventTriggerData>&>
         event_triggers,
@@ -1458,8 +1476,8 @@
         AttributionAggregatableTriggerData::CreateForTesting(
             absl::MakeUint128(/*high=*/i, /*low=*/0),
             /*source_keys=*/base::flat_set<std::string>{key_id},
-            /*filters=*/AttributionFilterData(),
-            /*not_filters=*/AttributionFilterData()));
+            /*filters=*/AttributionFilters(),
+            /*not_filters=*/AttributionFilters()));
     aggregatable_values.emplace(std::move(key_id), histogram_values[i]);
   }
 
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index b650015..5465ca8 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -623,6 +623,8 @@
 
 bool operator==(const AttributionFilterData& a, const AttributionFilterData& b);
 
+bool operator==(const AttributionFilters& a, const AttributionFilters& b);
+
 bool operator==(const CommonSourceInfo& a, const CommonSourceInfo& b);
 
 bool operator==(const AttributionInfo& a, const AttributionInfo& b);
@@ -674,6 +676,8 @@
 std::ostream& operator<<(std::ostream& out,
                          const AttributionFilterData& filter_data);
 
+std::ostream& operator<<(std::ostream& out, const AttributionFilters& filters);
+
 std::ostream& operator<<(std::ostream& out, const CommonSourceInfo& source);
 
 std::ostream& operator<<(std::ostream& out,
@@ -900,17 +904,16 @@
   ::testing::Matcher<uint64_t> data;
   ::testing::Matcher<int64_t> priority;
   ::testing::Matcher<absl::optional<uint64_t>> dedup_key;
-  ::testing::Matcher<const AttributionFilterData&> filters;
-  ::testing::Matcher<const AttributionFilterData&> not_filters;
+  ::testing::Matcher<const AttributionFilters&> filters;
+  ::testing::Matcher<const AttributionFilters&> not_filters;
 
   EventTriggerDataMatcherConfig() = delete;
   EventTriggerDataMatcherConfig(
       ::testing::Matcher<uint64_t> data = ::testing::_,
       ::testing::Matcher<int64_t> priority = ::testing::_,
       ::testing::Matcher<absl::optional<uint64_t>> dedup_key = ::testing::_,
-      ::testing::Matcher<const AttributionFilterData&> filters = ::testing::_,
-      ::testing::Matcher<const AttributionFilterData&> not_filters =
-          ::testing::_);
+      ::testing::Matcher<const AttributionFilters&> filters = ::testing::_,
+      ::testing::Matcher<const AttributionFilters&> not_filters = ::testing::_);
   ~EventTriggerDataMatcherConfig();
 };
 
@@ -920,7 +923,7 @@
 struct AttributionTriggerMatcherConfig {
   ::testing::Matcher<const url::Origin&> destination_origin = ::testing::_;
   ::testing::Matcher<const url::Origin&> reporting_origin = ::testing::_;
-  ::testing::Matcher<const AttributionFilterData&> filters = ::testing::_;
+  ::testing::Matcher<const AttributionFilters&> filters = ::testing::_;
   ::testing::Matcher<absl::optional<uint64_t>> debug_key = ::testing::_;
   ::testing::Matcher<const std::vector<AttributionTrigger::EventTriggerData>&>
       event_triggers = ::testing::_;
@@ -931,7 +934,7 @@
   AttributionTriggerMatcherConfig(
       ::testing::Matcher<const url::Origin&> destination_origin = ::testing::_,
       ::testing::Matcher<const url::Origin&> reporting_origin = ::testing::_,
-      ::testing::Matcher<const AttributionFilterData&> filters = ::testing::_,
+      ::testing::Matcher<const AttributionFilters&> filters = ::testing::_,
       ::testing::Matcher<absl::optional<uint64_t>> debug_key = ::testing::_,
       ::testing::Matcher<const std::vector<
           AttributionTrigger::EventTriggerData>&> event_triggers = ::testing::_,
diff --git a/content/browser/attribution_reporting/attribution_trigger.cc b/content/browser/attribution_reporting/attribution_trigger.cc
index e24fe8cd..e80e402 100644
--- a/content/browser/attribution_reporting/attribution_trigger.cc
+++ b/content/browser/attribution_reporting/attribution_trigger.cc
@@ -16,8 +16,8 @@
     uint64_t data,
     int64_t priority,
     absl::optional<uint64_t> dedup_key,
-    AttributionFilterData filters,
-    AttributionFilterData not_filters)
+    AttributionFilters filters,
+    AttributionFilters not_filters)
     : data(data),
       priority(priority),
       dedup_key(dedup_key),
@@ -27,8 +27,8 @@
 AttributionTrigger::AttributionTrigger(
     url::Origin destination_origin,
     url::Origin reporting_origin,
-    AttributionFilterData filters,
-    AttributionFilterData not_filters,
+    AttributionFilters filters,
+    AttributionFilters not_filters,
     absl::optional<uint64_t> debug_key,
     absl::optional<uint64_t> aggregatable_dedup_key,
     std::vector<EventTriggerData> event_triggers,
diff --git a/content/browser/attribution_reporting/attribution_trigger.h b/content/browser/attribution_reporting/attribution_trigger.h
index 12ad384..bc745048 100644
--- a/content/browser/attribution_reporting/attribution_trigger.h
+++ b/content/browser/attribution_reporting/attribution_trigger.h
@@ -86,17 +86,17 @@
 
     // The filters used to determine whether this `EventTriggerData'`s fields
     // are used.
-    AttributionFilterData filters;
+    AttributionFilters filters;
 
     // The negated filters used to determine whether this `EventTriggerData'`s
     // fields are used.
-    AttributionFilterData not_filters;
+    AttributionFilters not_filters;
 
     EventTriggerData(uint64_t data,
                      int64_t priority,
                      absl::optional<uint64_t> dedup_key,
-                     AttributionFilterData filters,
-                     AttributionFilterData not_filters);
+                     AttributionFilters filters,
+                     AttributionFilters not_filters);
   };
 
   // Should only be created with values that the browser process has already
@@ -105,8 +105,8 @@
   AttributionTrigger(
       url::Origin destination_origin,
       url::Origin reporting_origin,
-      AttributionFilterData filters,
-      AttributionFilterData not_filters,
+      AttributionFilters filters,
+      AttributionFilters not_filters,
       absl::optional<uint64_t> debug_key,
       absl::optional<uint64_t> aggregatable_dedup_key,
       std::vector<EventTriggerData> event_triggers,
@@ -123,9 +123,9 @@
 
   const url::Origin& reporting_origin() const { return reporting_origin_; }
 
-  const AttributionFilterData& filters() const { return filters_; }
+  const AttributionFilters& filters() const { return filters_; }
 
-  const AttributionFilterData& not_filters() const { return not_filters_; }
+  const AttributionFilters& not_filters() const { return not_filters_; }
 
   absl::optional<uint64_t> debug_key() const { return debug_key_; }
 
@@ -156,9 +156,9 @@
   // reports.
   url::Origin reporting_origin_;
 
-  AttributionFilterData filters_;
+  AttributionFilters filters_;
 
-  AttributionFilterData not_filters_;
+  AttributionFilters not_filters_;
 
   absl::optional<uint64_t> debug_key_;
 
diff --git a/content/browser/attribution_reporting/attribution_utils.cc b/content/browser/attribution_reporting/attribution_utils.cc
index c1fe43e..072ecb04 100644
--- a/content/browser/attribution_reporting/attribution_utils.cc
+++ b/content/browser/attribution_reporting/attribution_utils.cc
@@ -127,7 +127,7 @@
 
 bool AttributionFilterDataMatch(const AttributionFilterData& source,
                                 AttributionSourceType source_type,
-                                const AttributionFilterData& trigger,
+                                const AttributionFilters& trigger,
                                 bool negated) {
   // A filter is considered matched if the filter key is only present either on
   // the source or trigger, or the intersection of the filter values is
@@ -172,8 +172,8 @@
 
 bool AttributionFiltersMatch(const AttributionFilterData& source_filter_data,
                              AttributionSourceType source_type,
-                             const AttributionFilterData& trigger_filters,
-                             const AttributionFilterData& trigger_not_filters) {
+                             const AttributionFilters& trigger_filters,
+                             const AttributionFilters& trigger_not_filters) {
   return AttributionFilterDataMatch(source_filter_data, source_type,
                                     trigger_filters) &&
          AttributionFilterDataMatch(source_filter_data, source_type,
diff --git a/content/browser/attribution_reporting/attribution_utils.h b/content/browser/attribution_reporting/attribution_utils.h
index 4468a9be..06c5c1e 100644
--- a/content/browser/attribution_reporting/attribution_utils.h
+++ b/content/browser/attribution_reporting/attribution_utils.h
@@ -18,6 +18,7 @@
 namespace content {
 
 class AttributionFilterData;
+class AttributionFilters;
 class CommonSourceInfo;
 
 // Calculates the report time for a conversion associated with a given
@@ -41,14 +42,14 @@
 CONTENT_EXPORT bool AttributionFilterDataMatch(
     const AttributionFilterData& source,
     AttributionSourceType,
-    const AttributionFilterData& trigger,
+    const AttributionFilters& trigger,
     bool negated = false);
 
 CONTENT_EXPORT bool AttributionFiltersMatch(
     const AttributionFilterData& source_filter_data,
     AttributionSourceType,
-    const AttributionFilterData& trigger_filters,
-    const AttributionFilterData& trigger_not_filters);
+    const AttributionFilters& trigger_filters,
+    const AttributionFilters& trigger_not_filters);
 
 }  // namespace content
 
diff --git a/content/browser/attribution_reporting/attribution_utils_unittest.cc b/content/browser/attribution_reporting/attribution_utils_unittest.cc
index d42c268..91f8b3e 100644
--- a/content/browser/attribution_reporting/attribution_utils_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_utils_unittest.cc
@@ -11,193 +11,188 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
+namespace {
 
 TEST(AttributionUtilsTest, EmptyOrMissingAttributionFilters) {
-  auto empty_filter = AttributionFilterData();
+  const auto empty_filter = AttributionFilterValues();
 
-  auto empty_filter_values =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {}}});
-  ASSERT_TRUE(empty_filter_values.has_value());
+  const auto empty_filter_values = AttributionFilterValues({{"filter1", {}}});
 
-  auto one_filter =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {"value1"}}});
-  ASSERT_TRUE(one_filter.has_value());
+  const auto one_filter = AttributionFilterValues({{"filter1", {"value1"}}});
 
   const struct {
-    std::string description;
-    AttributionFilterData source_filter_data;
-    AttributionFilterData trigger_filter_data;
-    bool match_expected;
-  } kTestCases[] = {{"No source filters, no trigger filters", empty_filter,
-                     empty_filter, true},
-                    {"No source filters, trigger filter without values",
-                     empty_filter, *empty_filter_values, true},
-                    {"No source filters, trigger filter with value",
-                     empty_filter, *one_filter, true},
+    const char* description;
+    AttributionFilterValues filter_data;
+    AttributionFilterValues filters;
+  } kTestCases[] = {
+      {"No source filters, no trigger filters", empty_filter, empty_filter},
+      {"No source filters, trigger filter without values", empty_filter,
+       empty_filter_values},
+      {"No source filters, trigger filter with value", empty_filter,
+       one_filter},
 
-                    {"Source filter without values, no trigger filters",
-                     *empty_filter_values, empty_filter, true},
+      {"Source filter without values, no trigger filters", empty_filter_values,
+       empty_filter},
 
-                    {"Source filter with value, no trigger filters",
-                     *one_filter, empty_filter, true}};
+      {"Source filter with value, no trigger filters", one_filter,
+       empty_filter}};
 
   // Behavior should match for for negated and non-negated filters as it
   // requires a value on each side.
   for (const auto& test_case : kTestCases) {
-    EXPECT_EQ(test_case.match_expected,
-              AttributionFilterDataMatch(test_case.source_filter_data,
-                                         AttributionSourceType::kNavigation,
-                                         test_case.trigger_filter_data))
+    absl::optional<AttributionFilterData> filter_data =
+        AttributionFilterData::Create(test_case.filter_data);
+    ASSERT_TRUE(filter_data) << test_case.description;
+
+    absl::optional<AttributionFilters> filters =
+        AttributionFilters::Create(test_case.filters);
+    ASSERT_TRUE(filters) << test_case.description;
+
+    EXPECT_TRUE(AttributionFilterDataMatch(
+        *filter_data, AttributionSourceType::kNavigation, *filters))
         << test_case.description;
 
-    EXPECT_EQ(test_case.match_expected,
-              AttributionFilterDataMatch(test_case.source_filter_data,
-                                         AttributionSourceType::kNavigation,
-                                         test_case.trigger_filter_data,
-                                         /*negated=*/true))
+    EXPECT_TRUE(AttributionFilterDataMatch(
+        *filter_data, AttributionSourceType::kNavigation, *filters,
+        /*negated=*/true))
         << test_case.description << " with negation";
   }
 }
 
 TEST(AttributionUtilsTest, AttributionFilterDataMatch) {
-  auto empty_filter_values =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {}}});
-  ASSERT_TRUE(empty_filter_values.has_value());
+  const auto empty_filter_values = AttributionFilterValues({{"filter1", {}}});
 
-  auto one_filter =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {"value1"}}});
-  ASSERT_TRUE(one_filter.has_value());
+  const auto one_filter = AttributionFilterValues({{"filter1", {"value1"}}});
 
-  auto one_filter_different =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {"value2"}}});
-  ASSERT_TRUE(one_filter_different.has_value());
+  const auto one_filter_different =
+      AttributionFilterValues({{"filter1", {"value2"}}});
 
-  auto two_filters = AttributionFilterData::FromSourceFilterValues(
+  const auto two_filters = AttributionFilterValues(
       {{"filter1", {"value1"}}, {"filter2", {"value2"}}});
-  ASSERT_TRUE(two_filters.has_value());
 
-  auto one_mismatched_filter = AttributionFilterData::FromSourceFilterValues(
+  const auto one_mismatched_filter = AttributionFilterValues(
       {{"filter1", {"value1"}}, {"filter2", {"value3"}}});
-  ASSERT_TRUE(one_mismatched_filter.has_value());
 
-  auto two_mismatched_filter = AttributionFilterData::FromSourceFilterValues(
+  const auto two_mismatched_filter = AttributionFilterValues(
       {{"filter1", {"value3"}}, {"filter2", {"value4"}}});
-  ASSERT_TRUE(two_mismatched_filter.has_value());
 
   const struct {
-    std::string description;
-    AttributionFilterData source_filter_data;
-    AttributionFilterData trigger_filter_data;
+    const char* description;
+    AttributionFilterValues filter_data;
+    AttributionFilterValues filters;
     bool match_expected;
   } kTestCases[] = {
       {"Source filter without values, trigger filter with value",
-       *empty_filter_values, *one_filter, false},
+       empty_filter_values, one_filter, false},
       {"Source filter without values, trigger filter without values",
-       *empty_filter_values, *empty_filter_values, true},
-      {"Source filter with value, trigger filter without values", *one_filter,
-       *empty_filter_values, false},
+       empty_filter_values, empty_filter_values, true},
+      {"Source filter with value, trigger filter without values", one_filter,
+       empty_filter_values, false},
 
-      {"One filter with matching values", *one_filter, *one_filter, true},
-      {"One filter with no matching values", *one_filter, *one_filter_different,
+      {"One filter with matching values", one_filter, one_filter, true},
+      {"One filter with no matching values", one_filter, one_filter_different,
        false},
 
-      {"Two filters with matching values", *two_filters, *two_filters, true},
-      {"Two filters no matching values", *one_mismatched_filter,
-       *two_mismatched_filter, false},
+      {"Two filters with matching values", two_filters, two_filters, true},
+      {"Two filters no matching values", one_mismatched_filter,
+       two_mismatched_filter, false},
 
-      {"One filter not present in source, other matches", *one_filter,
-       *two_filters, true},
-      {"One filter not present in trigger, other matches", *two_filters,
-       *one_filter, true},
+      {"One filter not present in source, other matches", one_filter,
+       two_filters, true},
+      {"One filter not present in trigger, other matches", two_filters,
+       one_filter, true},
 
-      {"Two filters one filter no match", *two_filters, *one_mismatched_filter,
+      {"Two filters one filter no match", two_filters, one_mismatched_filter,
        false},
   };
 
   for (const auto& test_case : kTestCases) {
+    absl::optional<AttributionFilterData> filter_data =
+        AttributionFilterData::Create(test_case.filter_data);
+    ASSERT_TRUE(filter_data) << test_case.description;
+
+    absl::optional<AttributionFilters> filters =
+        AttributionFilters::Create(test_case.filters);
+    ASSERT_TRUE(filters) << test_case.description;
+
     EXPECT_EQ(test_case.match_expected,
-              AttributionFilterDataMatch(test_case.source_filter_data,
-                                         AttributionSourceType::kNavigation,
-                                         test_case.trigger_filter_data))
+              AttributionFilterDataMatch(
+                  *filter_data, AttributionSourceType::kNavigation, *filters))
         << test_case.description;
   }
 }
 
 TEST(AttributionUtilsTest, NegatedAttributionFilterDataMatch) {
-  auto empty_filter_values =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {}}});
-  ASSERT_TRUE(empty_filter_values.has_value());
+  const auto empty_filter_values = AttributionFilterValues({{"filter1", {}}});
 
-  auto one_filter =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {"value1"}}});
-  ASSERT_TRUE(one_filter.has_value());
+  const auto one_filter = AttributionFilterValues({{"filter1", {"value1"}}});
 
-  auto one_filter_different =
-      AttributionFilterData::FromSourceFilterValues({{"filter1", {"value2"}}});
-  ASSERT_TRUE(one_filter_different.has_value());
+  const auto one_filter_different =
+      AttributionFilterValues({{"filter1", {"value2"}}});
 
-  auto one_filter_one_different = AttributionFilterData::FromSourceFilterValues(
-      {{"filter1", {"value1", "value2"}}});
-  ASSERT_TRUE(one_filter_different.has_value());
+  const auto one_filter_one_different =
+      AttributionFilterValues({{"filter1", {"value1", "value2"}}});
 
-  auto one_filter_multiple_different =
-      AttributionFilterData::FromSourceFilterValues(
-          {{"filter1", {"value2", "value3"}}});
-  ASSERT_TRUE(one_filter_different.has_value());
+  const auto one_filter_multiple_different =
+      AttributionFilterValues({{"filter1", {"value2", "value3"}}});
 
-  auto two_filters = AttributionFilterData::FromSourceFilterValues(
+  const auto two_filters = AttributionFilterValues(
       {{"filter1", {"value1"}}, {"filter2", {"value2"}}});
-  ASSERT_TRUE(two_filters.has_value());
 
-  auto one_mismatched_filter = AttributionFilterData::FromSourceFilterValues(
+  const auto one_mismatched_filter = AttributionFilterValues(
       {{"filter1", {"value1"}}, {"filter2", {"value3"}}});
-  ASSERT_TRUE(one_mismatched_filter.has_value());
 
-  auto two_mismatched_filter = AttributionFilterData::FromSourceFilterValues(
+  const auto two_mismatched_filter = AttributionFilterValues(
       {{"filter1", {"value3"}}, {"filter2", {"value4"}}});
-  ASSERT_TRUE(two_mismatched_filter.has_value());
 
   const struct {
-    std::string description;
-    AttributionFilterData source_filter_data;
-    AttributionFilterData trigger_filter_data;
+    const char* description;
+    AttributionFilterValues filter_data;
+    AttributionFilterValues filters;
     bool match_expected;
   } kTestCases[] = {
       // True because there is not matching values within source.
       {"Source filter without values, trigger filter with value",
-       *empty_filter_values, *one_filter, true},
+       empty_filter_values, one_filter, true},
       {"Source filter without values, trigger filter without values",
-       *empty_filter_values, *empty_filter_values, false},
-      {"Source filter with value, trigger filter without values", *one_filter,
-       *empty_filter_values, true},
+       empty_filter_values, empty_filter_values, false},
+      {"Source filter with value, trigger filter without values", one_filter,
+       empty_filter_values, true},
 
-      {"One filter with matching values", *one_filter, *one_filter, false},
-      {"One filter with non-matching value", *one_filter, *one_filter_different,
+      {"One filter with matching values", one_filter, one_filter, false},
+      {"One filter with non-matching value", one_filter, one_filter_different,
        true},
-      {"One filter with one non-matching value", *one_filter,
-       *one_filter_one_different, false},
-      {"One filter with multiple non-matching values", *one_filter,
-       *one_filter_multiple_different, true},
+      {"One filter with one non-matching value", one_filter,
+       one_filter_one_different, false},
+      {"One filter with multiple non-matching values", one_filter,
+       one_filter_multiple_different, true},
 
-      {"Two filters with matching values", *two_filters, *two_filters, false},
-      {"Two filters no matching values", *one_mismatched_filter,
-       *two_mismatched_filter, true},
+      {"Two filters with matching values", two_filters, two_filters, false},
+      {"Two filters no matching values", one_mismatched_filter,
+       two_mismatched_filter, true},
 
-      {"One filter not present in source, other matches", *one_filter,
-       *two_filters, false},
-      {"One filter not present in trigger, other matches", *two_filters,
-       *one_filter, false},
+      {"One filter not present in source, other matches", one_filter,
+       two_filters, false},
+      {"One filter not present in trigger, other matches", two_filters,
+       one_filter, false},
 
-      {"Two filters one filter no match", *two_filters, *one_mismatched_filter,
+      {"Two filters one filter no match", two_filters, one_mismatched_filter,
        false},
   };
 
   for (const auto& test_case : kTestCases) {
+    absl::optional<AttributionFilterData> filter_data =
+        AttributionFilterData::Create(test_case.filter_data);
+    ASSERT_TRUE(filter_data) << test_case.description;
+
+    absl::optional<AttributionFilters> filters =
+        AttributionFilters::Create(test_case.filters);
+    ASSERT_TRUE(filters) << test_case.description;
+
     EXPECT_EQ(test_case.match_expected,
-              AttributionFilterDataMatch(test_case.source_filter_data,
-                                         AttributionSourceType::kNavigation,
-                                         test_case.trigger_filter_data,
-                                         /*negated=*/true))
+              AttributionFilterDataMatch(
+                  *filter_data, AttributionSourceType::kNavigation, *filters,
+                  /*negated=*/true))
         << test_case.description << " with negation";
   }
 }
@@ -206,28 +201,28 @@
   const struct {
     const char* description;
     AttributionSourceType source_type;
-    AttributionFilterData trigger_filters;
+    AttributionFilters filters;
     bool negated;
     bool match_expected;
   } kTestCases[] = {
       {
           .description = "empty-filters",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData(),
+          .filters = AttributionFilters(),
           .negated = false,
           .match_expected = true,
       },
       {
           .description = "empty-filters-negated",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData(),
+          .filters = AttributionFilters(),
           .negated = true,
           .match_expected = true,
       },
       {
           .description = "empty-filter-values",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData::CreateForTesting({
+          .filters = *AttributionFilters::Create({
               {AttributionFilterData::kSourceTypeFilterKey, {}},
           }),
           .negated = false,
@@ -236,7 +231,7 @@
       {
           .description = "empty-filter-values-negated",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData::CreateForTesting({
+          .filters = *AttributionFilters::Create({
               {AttributionFilterData::kSourceTypeFilterKey, {}},
           }),
           .negated = true,
@@ -245,36 +240,32 @@
       {
           .description = "same-source-type",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData::CreateForTesting({
-              {AttributionFilterData::kSourceTypeFilterKey, {"navigation"}},
-          }),
+          .filters = AttributionFilters::ForSourceType(
+              AttributionSourceType::kNavigation),
           .negated = false,
           .match_expected = true,
       },
       {
           .description = "same-source-type-negated",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData::CreateForTesting({
-              {AttributionFilterData::kSourceTypeFilterKey, {"navigation"}},
-          }),
+          .filters = AttributionFilters::ForSourceType(
+              AttributionSourceType::kNavigation),
           .negated = true,
           .match_expected = false,
       },
       {
           .description = "other-source-type",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData::CreateForTesting({
-              {AttributionFilterData::kSourceTypeFilterKey, {"event"}},
-          }),
+          .filters =
+              AttributionFilters::ForSourceType(AttributionSourceType::kEvent),
           .negated = false,
           .match_expected = false,
       },
       {
           .description = "other-source-type-negated",
           .source_type = AttributionSourceType::kNavigation,
-          .trigger_filters = AttributionFilterData::CreateForTesting({
-              {AttributionFilterData::kSourceTypeFilterKey, {"event"}},
-          }),
+          .filters =
+              AttributionFilters::ForSourceType(AttributionSourceType::kEvent),
           .negated = true,
           .match_expected = true,
       },
@@ -282,11 +273,12 @@
 
   for (const auto& test_case : kTestCases) {
     EXPECT_EQ(test_case.match_expected,
-              AttributionFilterDataMatch(
-                  AttributionFilterData(), test_case.source_type,
-                  test_case.trigger_filters, test_case.negated))
+              AttributionFilterDataMatch(AttributionFilterData(),
+                                         test_case.source_type,
+                                         test_case.filters, test_case.negated))
         << test_case.description;
   }
 }
 
+}  // namespace
 }  // namespace content
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index abfc3eb..b6189e5 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -314,6 +314,8 @@
   MSDH_INCONSISTENT_AUDIO_TYPE_AND_REQUESTED_FIELDS = 287,
   MSDH_INCONSISTENT_VIDEO_TYPE_AND_REQUESTED_FIELDS = 288,
   MSDH_SUPPRESS_LOCAL_AUDIO_PLAYBACK_BUT_AUDIO_NOT_REQUESTED = 289,
+  MSDH_HOTWORD_ENABLED_BUT_AUDIO_NOT_REQUESTED = 290,
+  MSDH_DISABLE_LOCAL_ECHO_BUT_AUDIO_NOT_REQUESTED = 291,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
index 3eb80765..8c60e1c 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
@@ -759,6 +759,14 @@
       return bad_message::
           MSDH_SUPPRESS_LOCAL_AUDIO_PLAYBACK_BUT_AUDIO_NOT_REQUESTED;
     }
+
+    if (controls.hotword_enabled) {
+      return bad_message::MSDH_HOTWORD_ENABLED_BUT_AUDIO_NOT_REQUESTED;
+    }
+
+    if (controls.disable_local_echo) {
+      return bad_message::MSDH_DISABLE_LOCAL_ECHO_BUT_AUDIO_NOT_REQUESTED;
+    }
   }
 
   return absl::nullopt;
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index 3b38c93f..be9ccac 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -590,68 +590,6 @@
   EXPECT_FALSE(video_device(/*stream_index=*/0u).has_value());
 }
 
-class MediaStreamDispatcherHostStreamTypeCombinationTest
-    : public MediaStreamDispatcherHostTest,
-      public ::testing::WithParamInterface<std::tuple<int, int>> {};
-
-TEST_P(MediaStreamDispatcherHostStreamTypeCombinationTest,
-       GenerateStreamWithStreamTypeCombination) {
-  using blink::mojom::MediaStreamType;
-
-  std::set<std::tuple<MediaStreamType, MediaStreamType>> kValidCombinations = {
-      {MediaStreamType::NO_SERVICE, MediaStreamType::NO_SERVICE},
-      {MediaStreamType::NO_SERVICE, MediaStreamType::DEVICE_VIDEO_CAPTURE},
-      {MediaStreamType::NO_SERVICE, MediaStreamType::GUM_TAB_VIDEO_CAPTURE},
-      {MediaStreamType::NO_SERVICE, MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE},
-      {MediaStreamType::NO_SERVICE, MediaStreamType::DISPLAY_VIDEO_CAPTURE},
-      {MediaStreamType::NO_SERVICE, MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET},
-      {MediaStreamType::NO_SERVICE,
-       MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB},
-      {MediaStreamType::DEVICE_AUDIO_CAPTURE, MediaStreamType::NO_SERVICE},
-      {MediaStreamType::DEVICE_AUDIO_CAPTURE,
-       MediaStreamType::DEVICE_VIDEO_CAPTURE},
-      {MediaStreamType::GUM_TAB_AUDIO_CAPTURE, MediaStreamType::NO_SERVICE},
-      {MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
-       MediaStreamType::GUM_TAB_VIDEO_CAPTURE},
-      {MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
-       MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE},
-      {MediaStreamType::DISPLAY_AUDIO_CAPTURE,
-       MediaStreamType::DISPLAY_VIDEO_CAPTURE},
-      {MediaStreamType::DISPLAY_AUDIO_CAPTURE,
-       MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB}};
-
-  blink::StreamControls controls;
-
-  controls.audio.stream_type =
-      static_cast<MediaStreamType>(std::get<0>(GetParam()));
-  controls.audio.requested =
-      (controls.audio.stream_type != MediaStreamType::NO_SERVICE);
-
-  controls.video.stream_type =
-      static_cast<MediaStreamType>(std::get<1>(GetParam()));
-  controls.video.requested =
-      (controls.video.stream_type != MediaStreamType::NO_SERVICE);
-
-  SetupFakeUI(true);
-  EXPECT_CALL(
-      *this, MockOnBadMessage(
-                 kProcessId, bad_message::MSDH_INVALID_STREAM_TYPE_COMBINATION))
-      .Times(!kValidCombinations.count(std::make_tuple(
-          controls.audio.stream_type, controls.video.stream_type)));
-  host_->OnGenerateStreams(kPageRequestId, controls);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    MediaStreamDispatcherHostStreamTypeCombinationTest,
-    ::testing::Combine(
-        ::testing::Range(
-            static_cast<int>(blink::mojom::MediaStreamType::NO_SERVICE),
-            static_cast<int>(blink::mojom::MediaStreamType::NUM_MEDIA_TYPES)),
-        ::testing::Range(
-            static_cast<int>(blink::mojom::MediaStreamType::NO_SERVICE),
-            static_cast<int>(blink::mojom::MediaStreamType::NUM_MEDIA_TYPES))));
-
 TEST_F(MediaStreamDispatcherHostTest,
        BadMessageIfAudioRequestedButTypeIsNoService) {
   using blink::mojom::MediaStreamType;
@@ -717,6 +655,48 @@
   host_->OnGenerateStreams(kPageRequestId, controls);
 }
 
+TEST_F(MediaStreamDispatcherHostTest,
+       BadMessageIfAudioNotRequestedAndHotwordEnabled) {
+  using blink::mojom::MediaStreamType;
+
+  blink::StreamControls controls;
+  controls.audio.requested = false;
+  controls.audio.stream_type = MediaStreamType::NO_SERVICE;
+  controls.video.requested = true;
+  controls.video.stream_type = MediaStreamType::DISPLAY_VIDEO_CAPTURE;
+  controls.hotword_enabled = true;
+
+  SetupFakeUI(true);
+
+  EXPECT_CALL(*this,
+              MockOnBadMessage(
+                  kProcessId,
+                  bad_message::MSDH_HOTWORD_ENABLED_BUT_AUDIO_NOT_REQUESTED))
+      .Times(1);
+  host_->OnGenerateStreams(kPageRequestId, controls);
+}
+
+TEST_F(MediaStreamDispatcherHostTest,
+       BadMessageIfAudioNotRequestedAndDisableLocalEcho) {
+  using blink::mojom::MediaStreamType;
+
+  blink::StreamControls controls;
+  controls.audio.requested = false;
+  controls.audio.stream_type = MediaStreamType::NO_SERVICE;
+  controls.video.requested = true;
+  controls.video.stream_type = MediaStreamType::DISPLAY_VIDEO_CAPTURE;
+  controls.disable_local_echo = true;
+
+  SetupFakeUI(true);
+
+  EXPECT_CALL(*this,
+              MockOnBadMessage(
+                  kProcessId,
+                  bad_message::MSDH_DISABLE_LOCAL_ECHO_BUT_AUDIO_NOT_REQUESTED))
+      .Times(1);
+  host_->OnGenerateStreams(kPageRequestId, controls);
+}
+
 // This test simulates a shutdown scenario: we don't setup a fake UI proxy for
 // MediaStreamManager, so it will create an ordinary one which will not find
 // a RenderFrameHostDelegate. This normally should only be the case at shutdown.
@@ -1395,4 +1375,66 @@
 }
 // TODO(crbug.com/1300883): Add test cases for multi stream generation.
 
+class MediaStreamDispatcherHostStreamTypeCombinationTest
+    : public MediaStreamDispatcherHostTest,
+      public ::testing::WithParamInterface<std::tuple<int, int>> {};
+
+TEST_P(MediaStreamDispatcherHostStreamTypeCombinationTest,
+       GenerateStreamWithStreamTypeCombination) {
+  using blink::mojom::MediaStreamType;
+
+  std::set<std::tuple<MediaStreamType, MediaStreamType>> kValidCombinations = {
+      {MediaStreamType::NO_SERVICE, MediaStreamType::NO_SERVICE},
+      {MediaStreamType::NO_SERVICE, MediaStreamType::DEVICE_VIDEO_CAPTURE},
+      {MediaStreamType::NO_SERVICE, MediaStreamType::GUM_TAB_VIDEO_CAPTURE},
+      {MediaStreamType::NO_SERVICE, MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE},
+      {MediaStreamType::NO_SERVICE, MediaStreamType::DISPLAY_VIDEO_CAPTURE},
+      {MediaStreamType::NO_SERVICE, MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET},
+      {MediaStreamType::NO_SERVICE,
+       MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB},
+      {MediaStreamType::DEVICE_AUDIO_CAPTURE, MediaStreamType::NO_SERVICE},
+      {MediaStreamType::DEVICE_AUDIO_CAPTURE,
+       MediaStreamType::DEVICE_VIDEO_CAPTURE},
+      {MediaStreamType::GUM_TAB_AUDIO_CAPTURE, MediaStreamType::NO_SERVICE},
+      {MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
+       MediaStreamType::GUM_TAB_VIDEO_CAPTURE},
+      {MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
+       MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE},
+      {MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+       MediaStreamType::DISPLAY_VIDEO_CAPTURE},
+      {MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+       MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB}};
+
+  blink::StreamControls controls;
+
+  controls.audio.stream_type =
+      static_cast<MediaStreamType>(std::get<0>(GetParam()));
+  controls.audio.requested =
+      (controls.audio.stream_type != MediaStreamType::NO_SERVICE);
+
+  controls.video.stream_type =
+      static_cast<MediaStreamType>(std::get<1>(GetParam()));
+  controls.video.requested =
+      (controls.video.stream_type != MediaStreamType::NO_SERVICE);
+
+  SetupFakeUI(true);
+  EXPECT_CALL(
+      *this, MockOnBadMessage(
+                 kProcessId, bad_message::MSDH_INVALID_STREAM_TYPE_COMBINATION))
+      .Times(!kValidCombinations.count(std::make_tuple(
+          controls.audio.stream_type, controls.video.stream_type)));
+  host_->OnGenerateStreams(kPageRequestId, controls);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    MediaStreamDispatcherHostStreamTypeCombinationTest,
+    ::testing::Combine(
+        ::testing::Range(
+            static_cast<int>(blink::mojom::MediaStreamType::NO_SERVICE),
+            static_cast<int>(blink::mojom::MediaStreamType::NUM_MEDIA_TYPES)),
+        ::testing::Range(
+            static_cast<int>(blink::mojom::MediaStreamType::NO_SERVICE),
+            static_cast<int>(blink::mojom::MediaStreamType::NUM_MEDIA_TYPES))));
+
 }  // namespace content
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index d2a2b20..4d97b04 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -1087,7 +1087,7 @@
   RequestExpectations request_not_in_list = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorManifestNotInManifestList,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST_LIST | FetchedEndpoint::MANIFEST};
 
   IdentityProviderParameters identity_provider{"https://not-in-list.example",
@@ -1112,7 +1112,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorManifestNotInManifestList,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST_LIST | FetchedEndpoint::MANIFEST};
   RunAuthTest(parameters, expectations, config);
 }
@@ -1124,7 +1124,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 
@@ -1146,7 +1146,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 
@@ -1182,7 +1182,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 }
@@ -1195,10 +1195,10 @@
       std::vector<IdentityProviderParameters>{identity_provider},
       /*prefer_auto_sign_in=*/false};
   MockConfiguration configuration = kConfigurationValid;
-  RequestExpectations expectations = {
-      RequestTokenStatus::kError, FederatedAuthRequestResult::kError,
-      /* selected_idp_config_url=*/absl::nullopt,
-      /*fetched_endpoints=*/0};
+  RequestExpectations expectations = {RequestTokenStatus::kError,
+                                      FederatedAuthRequestResult::kError,
+                                      /*selected_idp_config_url=*/absl::nullopt,
+                                      /*fetched_endpoints=*/0};
   RunAuthTest(request, expectations, configuration);
 
   histogram_tester_.ExpectUniqueSample(
@@ -1214,7 +1214,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::ACCOUNTS |
           FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -1228,7 +1228,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::ACCOUNTS |
           FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -1278,7 +1278,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
   std::vector<std::string> messages =
@@ -1407,7 +1407,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingIdTokenInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FETCH_ENDPOINT_ALL_REQUEST_TOKEN};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 }
@@ -1626,7 +1626,7 @@
   configuration.customized_dialog = true;
   RequestExpectations expectations = {
       RequestTokenStatus::kError, FederatedAuthRequestResult::kShouldEmbargo,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FETCH_ENDPOINT_ALL_REQUEST_TOKEN & ~FetchedEndpoint::TOKEN};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 
@@ -1688,7 +1688,7 @@
   RequestExpectations expectations = {
       /*return_status=*/absl::nullopt,
       /*devtools_issue_status=*/absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FETCH_ENDPOINT_ALL_REQUEST_TOKEN & ~FetchedEndpoint::TOKEN};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
   task_environment()->FastForwardBy(base::Minutes(10));
@@ -1747,7 +1747,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorRpPageNotVisible,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FETCH_ENDPOINT_ALL_REQUEST_TOKEN & ~FetchedEndpoint::TOKEN};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 
@@ -1759,10 +1759,10 @@
       std::make_pair(main_test_rfh()->GetLastCommittedOrigin(),
                      ApiPermissionStatus::BLOCKED_THIRD_PARTY_COOKIES_BLOCKED);
 
-  RequestExpectations expectations = {
-      RequestTokenStatus::kError, FederatedAuthRequestResult::kError,
-      /* selected_idp_config_url=*/absl::nullopt,
-      /*fetched_endpoints=*/0};
+  RequestExpectations expectations = {RequestTokenStatus::kError,
+                                      FederatedAuthRequestResult::kError,
+                                      /*selected_idp_config_url=*/absl::nullopt,
+                                      /*fetched_endpoints=*/0};
   RunAuthTest(kDefaultRequestParameters, expectations, kConfigurationValid);
 
   histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.RequestIdToken",
@@ -1777,10 +1777,10 @@
       std::make_pair(main_test_rfh()->GetLastCommittedOrigin(),
                      ApiPermissionStatus::BLOCKED_VARIATIONS);
 
-  RequestExpectations expectations = {
-      RequestTokenStatus::kError, FederatedAuthRequestResult::kError,
-      /* selected_idp_config_url=*/absl::nullopt,
-      /*fetched_endpoints=*/0};
+  RequestExpectations expectations = {RequestTokenStatus::kError,
+                                      FederatedAuthRequestResult::kError,
+                                      /*selected_idp_config_url=*/absl::nullopt,
+                                      /*fetched_endpoints=*/0};
   RunAuthTest(kDefaultRequestParameters, expectations, kConfigurationValid);
 
   histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.RequestIdToken",
@@ -1797,11 +1797,10 @@
 
   MockConfiguration configuration = kConfigurationValid;
   configuration.wait_for_callback = false;
-  RequestExpectations expectations = {
-      /*return_status=*/absl::nullopt,
-      /*devtools_issue_status*/ absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
-      /*fetched_endpoints=*/0};
+  RequestExpectations expectations = {/*return_status=*/absl::nullopt,
+                                      /*devtools_issue_status*/ absl::nullopt,
+                                      /*selected_idp_config_url=*/absl::nullopt,
+                                      /*fetched_endpoints=*/0};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 
   // Delete the request before DelayTimer kicks in.
@@ -1823,11 +1822,10 @@
 
   MockConfiguration configuration = kConfigurationValid;
   configuration.wait_for_callback = false;
-  RequestExpectations expectations = {
-      /*return_status=*/absl::nullopt,
-      /*devtools_issue_status*/ absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
-      /*fetched_endpoints=*/0};
+  RequestExpectations expectations = {/*return_status=*/absl::nullopt,
+                                      /*devtools_issue_status*/ absl::nullopt,
+                                      /*selected_idp_config_url=*/absl::nullopt,
+                                      /*fetched_endpoints=*/0};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 
   // Abort the request before DelayTimer kicks in.
@@ -1966,7 +1964,7 @@
 TEST_F(FederatedAuthRequestImplTest, RequestEmbargo) {
   RequestExpectations expectations = {
       RequestTokenStatus::kError, FederatedAuthRequestResult::kShouldEmbargo,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FETCH_ENDPOINT_ALL_REQUEST_TOKEN & ~FetchedEndpoint::TOKEN};
 
   MockConfiguration configuration = kConfigurationValid;
@@ -2012,7 +2010,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorDisabledInSettings,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       /*fetched_endpoints=*/0};
   RunAuthTest(kDefaultRequestParameters, expectations, kConfigurationValid);
 }
@@ -2055,7 +2053,7 @@
   RequestExpectations expectation = {
       /*return_status=*/absl::nullopt,
       /*devtools_issue_status*/ absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       /*fetched_endpoints=*/
       fedcm_disabled
           ? 0
@@ -2107,7 +2105,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorDisabledInSettings,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FETCH_ENDPOINT_ALL_REQUEST_TOKEN & ~FetchedEndpoint::TOKEN};
 
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -2243,7 +2241,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       /*devtools_issue_status*/ absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::CLIENT_METADATA |
           FetchedEndpoint::MANIFEST_LIST | FetchedEndpoint::ACCOUNTS};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -2269,7 +2267,7 @@
   RequestExpectations expectations = {
       /*return_status*/ absl::nullopt,
       /*devtools_issue_status*/ absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::CLIENT_METADATA |
           FetchedEndpoint::MANIFEST_LIST | FetchedEndpoint::ACCOUNTS};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -2336,7 +2334,7 @@
   RequestExpectations expectations = {
       RequestTokenStatus::kError,
       FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::ACCOUNTS |
           FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -2369,7 +2367,7 @@
       ParseStatus::kInvalidResponseError;
   RequestExpectations expectations = {
       RequestTokenStatus::kError, FederatedAuthRequestResult::kError,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST | FetchedEndpoint::ACCOUNTS |
           FetchedEndpoint::MANIFEST_LIST};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
@@ -2392,10 +2390,10 @@
   EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _, _, _))
       .Times(0);
   MockConfiguration configuration = kConfigurationValid;
-  RequestExpectations expectations = {
-      RequestTokenStatus::kError, FederatedAuthRequestResult::kError,
-      /* selected_idp_config_url=*/absl::nullopt,
-      /*fetched_endpoints=*/0};
+  RequestExpectations expectations = {RequestTokenStatus::kError,
+                                      FederatedAuthRequestResult::kError,
+                                      /*selected_idp_config_url=*/absl::nullopt,
+                                      /*fetched_endpoints=*/0};
   RunAuthTest(kDefaultRequestParameters, expectations, configuration);
 }
 
@@ -2482,7 +2480,7 @@
 
   RequestExpectations expectations = {
       RequestTokenStatus::kError, absl::nullopt,
-      /* selected_idp_config_url=*/absl::nullopt,
+      /*selected_idp_config_url=*/absl::nullopt,
       FetchedEndpoint::MANIFEST_LIST | FetchedEndpoint::MANIFEST};
 
   RunAuthTest(parameters, expectations, configuration);
diff --git a/content/public/browser/ax_inspect_factory_auralinux.cc b/content/public/browser/ax_inspect_factory_auralinux.cc
index da6ea88..7db74b0 100644
--- a/content/public/browser/ax_inspect_factory_auralinux.cc
+++ b/content/public/browser/ax_inspect_factory_auralinux.cc
@@ -5,9 +5,9 @@
 #include "content/public/browser/ax_inspect_factory.h"
 
 #include "base/notreached.h"
-#include "content/browser/accessibility/accessibility_event_recorder_auralinux.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder_auralinux.h"
 #include "ui/accessibility/platform/inspect/ax_tree_formatter_auralinux.h"
 
 namespace content {
@@ -59,8 +59,8 @@
 
   switch (type) {
     case ui::AXApiType::kLinux:
-      return std::make_unique<AccessibilityEventRecorderAuraLinux>(manager, pid,
-                                                                   selector);
+      return std::make_unique<ui::AXEventRecorderAuraLinux>(manager, pid,
+                                                            selector);
     default:
       NOTREACHED() << "Unsupported API type " << static_cast<std::string>(type);
   }
diff --git a/content/services/auction_worklet/direct_from_seller_signals_requester.cc b/content/services/auction_worklet/direct_from_seller_signals_requester.cc
index 12094ca..297c6d5 100644
--- a/content/services/auction_worklet/direct_from_seller_signals_requester.cc
+++ b/content/services/auction_worklet/direct_from_seller_signals_requester.cc
@@ -15,7 +15,9 @@
 #include "base/check.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "content/services/auction_worklet/auction_downloader.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "net/http/http_response_headers.h"
@@ -59,6 +61,15 @@
   return absl::nullopt;
 }
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class DirectFromSellerSignalsRequestType {
+  kNetworkServiceFetch = 0,
+  kCache = 1,
+  kCoalesced = 2,
+  kMaxValue = kCoalesced,
+};
+
 }  // namespace
 
 DirectFromSellerSignalsRequester::Result::Result() = default;
@@ -178,6 +189,9 @@
 
   if (cached_result_.signals_url() == signals_url) {
     // Request completed from cache -- done.
+    base::UmaHistogramEnumeration(
+        "Ads.InterestGroup.Auction.DirectFromSellerSignals.RequestType",
+        DirectFromSellerSignalsRequestType::kCache);
     request->RunCallbackAsync(cached_result_);
     return request;
   }
@@ -193,8 +207,15 @@
             AuctionDownloader::MimeType::kJson,
             base::BindOnce(
                 &DirectFromSellerSignalsRequester::OnSignalsDownloaded,
-                base::Unretained(this), signals_url))));
+                base::Unretained(this), signals_url, base::TimeTicks::Now()))));
     DCHECK(inserted);
+    base::UmaHistogramEnumeration(
+        "Ads.InterestGroup.Auction.DirectFromSellerSignals.RequestType",
+        DirectFromSellerSignalsRequestType::kNetworkServiceFetch);
+  } else {
+    base::UmaHistogramEnumeration(
+        "Ads.InterestGroup.Auction.DirectFromSellerSignals.RequestType",
+        DirectFromSellerSignalsRequestType::kCoalesced);
   }
   // A download is running for `signals_url` -- register to receive the result,
   // and register the iterator with `request` so that the Request destructor can
@@ -223,9 +244,20 @@
 
 void DirectFromSellerSignalsRequester::OnSignalsDownloaded(
     GURL signals_url,
+    base::TimeTicks start_time,
     std::unique_ptr<std::string> response_body,
     scoped_refptr<net::HttpResponseHeaders> headers,
     absl::optional<std::string> error) {
+  if (response_body) {
+    // The request size isn't very meaningful, since the request is served from
+    // a subresource bundle, so don't record the request size.
+    base::UmaHistogramCounts10M(
+        "Ads.InterestGroup.Net.ResponseSizeBytes.DirectFromSellerSignals",
+        response_body->size());
+    base::UmaHistogramTimes(
+        "Ads.InterestGroup.Net.DownloadTime.DirectFromSellerSignals",
+        base::TimeTicks::Now() - start_time);
+  }
   Result result(signals_url, std::move(response_body), std::move(headers),
                 std::move(error));
   cached_result_ = result;
@@ -270,7 +302,4 @@
     coalesced_downloads_.erase(map_it);
 }
 
-// TODO(crbug.com/1320908): Add UMA for request and download size.
-// TODO(crbug.com/1320908): Add UMA for cache hit rate.
-
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/direct_from_seller_signals_requester.h b/content/services/auction_worklet/direct_from_seller_signals_requester.h
index 105b6fe8..4947480d 100644
--- a/content/services/auction_worklet/direct_from_seller_signals_requester.h
+++ b/content/services/auction_worklet/direct_from_seller_signals_requester.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "base/types/strong_alias.h"
 #include "content/common/content_export.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
@@ -228,6 +229,7 @@
   // Validates headers, caches the results, and calls all callbacks held in
   // Result objects in `coalesced_downloads_` that are waiting on the URL.
   void OnSignalsDownloaded(GURL signals_url,
+                           base::TimeTicks start_time,
                            std::unique_ptr<std::string> response_body,
                            scoped_refptr<net::HttpResponseHeaders> headers,
                            absl::optional<std::string> error);
diff --git a/content/test/attribution_simulator_input_parser.cc b/content/test/attribution_simulator_input_parser.cc
index ac3bc3e6..6e919e16 100644
--- a/content/test/attribution_simulator_input_parser.cc
+++ b/content/test/attribution_simulator_input_parser.cc
@@ -284,8 +284,8 @@
         ParseOrigin(trigger_dict, "destination_origin");
 
     absl::optional<uint64_t> debug_key;
-    AttributionFilterData filters;
-    AttributionFilterData not_filters;
+    AttributionFilters filters;
+    AttributionFilters not_filters;
     std::vector<AttributionTrigger::EventTriggerData> event_triggers;
     std::vector<AttributionAggregatableTriggerData> aggregatable_trigger_data;
     AttributionAggregatableValues aggregatable_values;
@@ -297,12 +297,8 @@
             base::BindLambdaForTesting(
                 [&](const base::Value::Dict& dict) {
                   debug_key = ParseOptionalUint64(dict, "debug_key");
-                  filters = ParseFilterData(
-                      dict, "filters",
-                      &AttributionFilterData::FromTriggerFilterValues);
-                  not_filters = ParseFilterData(
-                      dict, "not_filters",
-                      &AttributionFilterData::FromTriggerFilterValues);
+                  filters = ParseFilters(dict, "filters");
+                  not_filters = ParseFilters(dict, "not_filters");
                   event_triggers = ParseEventTriggers(dict);
 
                   aggregatable_trigger_data =
@@ -359,12 +355,9 @@
           absl::optional<uint64_t> dedup_key =
               ParseOptionalUint64(dict, "deduplication_key");
 
-          AttributionFilterData filters = ParseFilterData(
-              dict, "filters", &AttributionFilterData::FromTriggerFilterValues);
+          AttributionFilters filters = ParseFilters(dict, "filters");
 
-          AttributionFilterData not_filters =
-              ParseFilterData(dict, "not_filters",
-                              &AttributionFilterData::FromTriggerFilterValues);
+          AttributionFilters not_filters = ParseFilters(dict, "not_filters");
 
           if (has_error())
             return;
@@ -499,23 +492,18 @@
     return true;
   }
 
-  using FromFilterValuesFunc = absl::optional<AttributionFilterData>(
-      AttributionFilterData::FilterValues&&);
-
-  AttributionFilterData ParseFilterData(
-      const base::Value::Dict& dict,
-      base::StringPiece key,
-      FromFilterValuesFunc from_filter_values) {
+  AttributionFilters ParseFilters(const base::Value::Dict& dict,
+                                  base::StringPiece key) {
     auto context = PushContext(key);
 
     const base::Value* value = dict.Find(key);
     if (!value)
-      return AttributionFilterData();
+      return AttributionFilters();
 
     if (!EnsureDictionary(*value))
-      return AttributionFilterData();
+      return AttributionFilters();
 
-    AttributionFilterData::FilterValues::container_type container;
+    AttributionFilterValues::container_type container;
     for (auto [filter, values_list] : value->GetDict()) {
       auto filter_context = PushContext(filter);
       std::vector<std::string> values;
@@ -532,13 +520,13 @@
       container.emplace_back(filter, std::move(values));
     }
 
-    absl::optional<AttributionFilterData> filter_data =
-        from_filter_values(std::move(container));
+    absl::optional<AttributionFilters> filters =
+        AttributionFilters::Create(std::move(container));
     // TODO(apaseltiner): Provide more detailed information.
-    if (!filter_data)
+    if (!filters)
       *Error() << "invalid";
 
-    return std::move(filter_data).value_or(AttributionFilterData());
+    return std::move(filters).value_or(AttributionFilters());
   }
 
   absl::uint128 ParseAggregationKey(const base::Value& key_value) {
@@ -616,13 +604,11 @@
                 }
               }
 
-              AttributionFilterData filters = ParseFilterData(
-                  trigger_dict, "filters",
-                  &AttributionFilterData::FromTriggerFilterValues);
+              AttributionFilters filters =
+                  ParseFilters(trigger_dict, "filters");
 
-              AttributionFilterData not_filters = ParseFilterData(
-                  trigger_dict, "not_filters",
-                  &AttributionFilterData::FromTriggerFilterValues);
+              AttributionFilters not_filters =
+                  ParseFilters(trigger_dict, "not_filters");
 
               auto trigger_data = AttributionAggregatableTriggerData::Create(
                   key, std::move(source_keys), std::move(filters),
diff --git a/content/test/attribution_simulator_input_parser_unittest.cc b/content/test/attribution_simulator_input_parser_unittest.cc
index fcb8247b..64e57fa 100644
--- a/content/test/attribution_simulator_input_parser_unittest.cc
+++ b/content/test/attribution_simulator_input_parser_unittest.cc
@@ -216,11 +216,10 @@
                   .SetPriority(0)             // default
                   .SetDebugKey(absl::nullopt)  // default
                   .SetDebugReporting(false)    // default
-                  .SetFilterData(
-                      *AttributionFilterData::FromSourceFilterValues({
-                          {"a", {}},
-                          {"b", {"c", "d"}},
-                      }))
+                  .SetFilterData(*AttributionFilterData::Create({
+                      {"a", {}},
+                      {"b", {"c", "d"}},
+                  }))
                   .Build(),
               _),
           Pair(
@@ -355,12 +354,12 @@
                       /*reporting_origin=*/
                       url::Origin::Create(GURL("https://a.r.test")),
                       /*filters=*/
-                      *AttributionFilterData::FromTriggerFilterValues({
+                      *AttributionFilters::Create({
                           {"a", {"b", "c"}},
                           {"d", {}},
                       }),
                       /*not_filters=*/
-                      *AttributionFilterData::FromTriggerFilterValues({
+                      *AttributionFilters::Create({
                           {"e", {"f"}},
                       }),
                       /*debug_key=*/14,
@@ -371,19 +370,19 @@
                               /*priority=*/-5,
                               /*dedup_key=*/123,
                               /*filters=*/
-                              *AttributionFilterData::FromTriggerFilterValues({
+                              *AttributionFilters::Create({
                                   {"x", {"y"}},
                               }),
                               /*not_filters=*/
-                              *AttributionFilterData::FromTriggerFilterValues({
+                              *AttributionFilters::Create({
                                   {"z", {}},
                               })),
                           AttributionTrigger::EventTriggerData(
                               /*data=*/0,
                               /*priority=*/0,
                               /*dedup_key=*/absl::nullopt,
-                              /*filters=*/AttributionFilterData(),
-                              /*not_filters=*/AttributionFilterData()),
+                              /*filters=*/AttributionFilters(),
+                              /*not_filters=*/AttributionFilters()),
                       },
                       /*aggregatable_trigger_data=*/{},
                       /*aggregatable_values=*/AttributionAggregatableValues()),
@@ -397,8 +396,8 @@
                       url::Origin::Create(GURL("https://a.d2.test")),
                       /*reporting_origin=*/
                       url::Origin::Create(GURL("https://b.r.test")),
-                      /*filters=*/AttributionFilterData(),
-                      /*not_filters=*/AttributionFilterData(),
+                      /*filters=*/AttributionFilters(),
+                      /*not_filters=*/AttributionFilters(),
                       /*debug_key=*/absl::nullopt,
                       /*aggregatable_dedup_key=*/absl::nullopt,
                       /*event_triggers=*/{},
@@ -414,16 +413,16 @@
                       url::Origin::Create(GURL("https://a.d2.test")),
                       /*reporting_origin=*/
                       url::Origin::Create(GURL("https://b.r.test")),
-                      /*filters=*/AttributionFilterData(),
-                      /*not_filters=*/AttributionFilterData(),
+                      /*filters=*/AttributionFilters(),
+                      /*not_filters=*/AttributionFilters(),
                       /*debug_key=*/absl::nullopt,
                       /*aggregatable_dedup_key=*/789,
                       /*event_triggers=*/{},
                       {AttributionAggregatableTriggerData::CreateForTesting(
                           absl::MakeUint128(/*high=*/0, /*low=*/1),
                           /*source_keys=*/{"a"},
-                          /*filters=*/AttributionFilterData(),
-                          /*not_filters=*/AttributionFilterData())},
+                          /*filters=*/AttributionFilters(),
+                          /*not_filters=*/AttributionFilters())},
                       /*aggregatable_values=*/
                       AttributionAggregatableValues::CreateForTesting(
                           {{"a", 1}})),
diff --git a/content/test/data/accessibility/accdescription/description-ignores-slot-expected-blink.txt b/content/test/data/accessibility/accdescription/description-ignores-slot-expected-blink.txt
deleted file mode 100644
index 08fb16d6..0000000
--- a/content/test/data/accessibility/accdescription/description-ignores-slot-expected-blink.txt
+++ /dev/null
@@ -1 +0,0 @@
-button description='description' name='foobar' nameFrom=contents descriptionFrom=relatedElement
diff --git a/content/test/data/accessibility/accdescription/description-ignores-slot.html b/content/test/data/accessibility/accdescription/description-ignores-slot.html
deleted file mode 100644
index c9e5c5c..0000000
--- a/content/test/data/accessibility/accdescription/description-ignores-slot.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-@BLINK-ALLOW:description=*
-@BLINK-ALLOW:descriptionFrom=*
--->
-<!-- AccName calculation should ignore slot element -->
-<template id="template">
-  <slot id="slot" aria-label="label on slot" name="my-slot">default content</slot>
-  <button aria-describedby="slot" id="test">foobar</button>
-</template>
-
-<my-element>
-  <b slot="my-slot">description</b>
-</my-element>
-
-<script>
-customElements.define(
-    'my-element',
-    class extends HTMLElement {
-        constructor() {
-            super();
-            let template = document.getElementById('template');
-            let templateContent = template.content;
-
-            const shadowRoot = this.attachShadow({mode: 'open'})
-                  .appendChild(templateContent.cloneNode(true));
-        }
-    }
-);
-</script>
diff --git a/content/test/data/accessibility/accname/name-ignores-slot-expected-blink.txt b/content/test/data/accessibility/accname/name-ignores-slot-expected-blink.txt
deleted file mode 100644
index c5cb2c06..0000000
--- a/content/test/data/accessibility/accname/name-ignores-slot-expected-blink.txt
+++ /dev/null
@@ -1 +0,0 @@
-button name='one two three' nameFrom=contents
diff --git a/content/test/data/accessibility/accname/name-ignores-slot.html b/content/test/data/accessibility/accname/name-ignores-slot.html
deleted file mode 100644
index 242afd79..0000000
--- a/content/test/data/accessibility/accname/name-ignores-slot.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-@BLINK-ALLOW:name=*
-@BLINK-ALLOW:nameFrom=*
--->
-<!-- AccName calculation should ignore slot element -->
-<template id="template">
-  <button id="test">one <slot aria-label="label" name="my-slot"></slot> three</button>
-</template>
-
-<my-element>
-  <b slot="my-slot">two</b>
-</my-element>
-
-<script>
-customElements.define(
-    'my-element',
-    class extends HTMLElement {
-        constructor() {
-            super();
-            let template = document.getElementById('template');
-            let templateContent = template.content;
-
-            const shadowRoot = this.attachShadow({mode: 'open'})
-                  .appendChild(templateContent.cloneNode(true));
-        }
-    }
-);
-</script>
diff --git a/content/web_test/renderer/web_frame_test_proxy.cc b/content/web_test/renderer/web_frame_test_proxy.cc
index 0dd00b8..40deb99 100644
--- a/content/web_test/renderer/web_frame_test_proxy.cc
+++ b/content/web_test/renderer/web_frame_test_proxy.cc
@@ -351,11 +351,6 @@
       level = "MESSAGE";
   }
   std::string console_message(std::string("CONSOLE ") + level + ": ");
-  // Console messages shouldn't be included in the expected output for
-  // web-platform-tests because they may create non-determinism not
-  // intended by the test author. They are still included in the stderr
-  // output for debug purposes.
-  bool dump_to_stderr = test_runner()->IsWebPlatformTestsMode();
   if (!message.text.IsEmpty()) {
     std::string new_message;
     new_message = message.text.Utf8();
@@ -368,11 +363,13 @@
   }
   console_message += "\n";
 
-  if (dump_to_stderr) {
-    test_runner()->PrintMessageToStderr(console_message);
-  } else {
+  // Console messages shouldn't be included in the expected output for
+  // web-platform-tests because they may create non-determinism not
+  // intended by the test author. They are still included in the stderr
+  // output for debugging purposes.
+  test_runner()->PrintMessageToStderr(console_message);
+  if (!test_runner()->IsWebPlatformTestsMode())
     test_runner()->PrintMessage(console_message);
-  }
 }
 
 void WebFrameTestProxy::DidStartLoading() {
diff --git a/device/vr/android/arcore/arcore_gl.cc b/device/vr/android/arcore/arcore_gl.cc
index 27e8e85..779fd57 100644
--- a/device/vr/android/arcore/arcore_gl.cc
+++ b/device/vr/android/arcore/arcore_gl.cc
@@ -577,8 +577,7 @@
 
   // Also calculate the inverse projection which is needed for converting
   // screen touches to world rays.
-  bool has_inverse = projection_.GetInverse(&inverse_projection_);
-  DCHECK(has_inverse);
+  inverse_projection_ = projection_.GetCheckedInverse();
 
   // VRFieldOfView wants positive angles.
   mojom::VRFieldOfViewPtr field_of_view = mojom::VRFieldOfView::New();
diff --git a/device/vr/android/gvr/gvr_utils.cc b/device/vr/android/gvr/gvr_utils.cc
index 8da5ac3..00d5d69b 100644
--- a/device/vr/android/gvr/gvr_utils.cc
+++ b/device/vr/android/gvr/gvr_utils.cc
@@ -73,9 +73,7 @@
     gvr::Mat4f eye_mat = gvr_api->GetEyeFromHeadMatrix(eye);
     gfx::Transform eye_from_head;
     device::gvr_utils::GvrMatToTransform(eye_mat, &eye_from_head);
-    gfx::Transform head_from_eye;
-    bool is_invertible = eye_from_head.GetInverse(&head_from_eye);
-    DCHECK(is_invertible);
+    gfx::Transform head_from_eye = eye_from_head.GetCheckedInverse();
 
     view->mojo_from_view = mojo_from_head * head_from_eye;
   }
diff --git a/docs/testing/testing_in_chromium.md b/docs/testing/testing_in_chromium.md
index a279c75..4166d66 100644
--- a/docs/testing/testing_in_chromium.md
+++ b/docs/testing/testing_in_chromium.md
@@ -168,7 +168,7 @@
 
 ## How to deal with flaky tests
 
-Go to [Flake Portal] to find reports about flaky tests in your projects.
+Go to [LUCI Analysis] to find reports about flaky tests in your projects.
 
 * [Addressing Flaky GTests](./gtest_flake_tips.md)
 * [Addressing Flaky Web Tests](./web_tests_addressing_flake.md)
@@ -188,7 +188,7 @@
 [Tast]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/README.md
 [Web Tests]: ./web_tests.md
 [crbug/611756]: https://bugs.chromium.org/p/chromium/issues/detail?id=611756
-[Flake Portal]: https://analysis.chromium.org/p/chromium/flake-portal
+[LUCI Analysis]: https://luci-analysis.appspot.com/
 [Write Fuzz Target]: https://chromium.googlesource.com/chromium/src/+/main/testing/libfuzzer/getting_started.md#write-fuzz-target
 [Telemetry: Run benchmarks locally]: https://chromium.googlesource.com/catapult/+/HEAD/telemetry/docs/run_benchmarks_locally.md
 [Run fuzz target locally]: https://chromium.googlesource.com/chromium/src/+/main/testing/libfuzzer/getting_started.md#build-and-run-fuzz-target-locally
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index feaf34d..a750d1b 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -612,11 +612,11 @@
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics",
+    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.element",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.input.virtualkeyboard",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mediacodec",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.policy",
     "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
     "//ui/gfx",
     "//ui/ozone",
@@ -627,7 +627,7 @@
   additional_manifest_fragments = [
     "//build/config/fuchsia/test/fonts.shard.test-cml",
     "//build/config/fuchsia/test/network.shard.test-cml",
-    "//build/config/fuchsia/test/test_ui_stack.shard.test-cml",
+    "//build/config/fuchsia/test/gfx_test_ui_stack.shard.test-cml",
     "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
   ]
 
diff --git a/fuchsia_web/webengine/browser/ax_tree_converter_unittest.cc b/fuchsia_web/webengine/browser/ax_tree_converter_unittest.cc
index 6f23d1e..cbb1e097 100644
--- a/fuchsia_web/webengine/browser/ax_tree_converter_unittest.cc
+++ b/fuchsia_web/webengine/browser/ax_tree_converter_unittest.cc
@@ -103,9 +103,7 @@
 std::pair<ui::AXNodeData, Node> CreateSemanticNodeAllFieldsSet() {
   ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
   relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
-  relative_bounds.transform =
-      std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
-  relative_bounds.transform->MakeIdentity();
+  relative_bounds.transform = std::make_unique<gfx::Transform>();
   relative_bounds.offset_container_id = -1;
   auto ax_node_data = CreateAXNodeData(
       ax::mojom::Role::kButton, ax::mojom::Action::kFocus,
@@ -342,9 +340,7 @@
 TEST_F(AXTreeConverterTest, FieldMismatch) {
   ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
   relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
-  relative_bounds.transform =
-      std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
-  relative_bounds.transform->MakeIdentity();
+  relative_bounds.transform = std::make_unique<gfx::Transform>();
   auto source_node_data = CreateAXNodeData(
       ax::mojom::Role::kHeader, ax::mojom::Action::kSetValue,
       std::vector<int32_t>{kChildId1, kChildId2, kChildId3}, relative_bounds,
@@ -398,9 +394,7 @@
 TEST_F(AXTreeConverterTest, LocationFieldRespectsTypeInvariants) {
   ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
   relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
-  relative_bounds.transform =
-      std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
-  relative_bounds.transform->MakeIdentity();
+  relative_bounds.transform = std::make_unique<gfx::Transform>();
   auto source_node_data = CreateAXNodeData(
       ax::mojom::Role::kHeader, ax::mojom::Action::kSetValue,
       std::vector<int32_t>{kChildId1, kChildId2, kChildId3}, relative_bounds,
@@ -616,9 +610,7 @@
   node_data.id = 1;
   ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
   relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
-  relative_bounds.transform =
-      std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
-  relative_bounds.transform->MakeIdentity();
+  relative_bounds.transform = std::make_unique<gfx::Transform>();
   relative_bounds.offset_container_id = -1;
 
   node_data.relative_bounds = relative_bounds;
diff --git a/fuchsia_web/webengine/browser/frame_impl.cc b/fuchsia_web/webengine/browser/frame_impl.cc
index 1570a5d..9ac69da 100644
--- a/fuchsia_web/webengine/browser/frame_impl.cc
+++ b/fuchsia_web/webengine/browser/frame_impl.cc
@@ -785,7 +785,7 @@
 }
 
 void FrameImpl::CreateView(fuchsia::ui::views::ViewToken view_token) {
-  scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New();
+  auto view_ref_pair = scenic::ViewRefPair::New();
   CreateViewWithViewRef(std::move(view_token),
                         std::move(view_ref_pair.control_ref),
                         std::move(view_ref_pair.view_ref));
diff --git a/fuchsia_web/webengine/browser/frame_impl_browsertest.cc b/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
index f79a1ee..b14cc32 100644
--- a/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
+++ b/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fuchsia/ui/policy/cpp/fidl.h>
+#include <fuchsia/element/cpp/fidl.h>
 #include <lib/ui/scenic/cpp/view_token_pair.h>
 
+#include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/mem_buffer_util.h"
 #include "base/test/test_future.h"
 #include "build/build_config.h"
@@ -22,6 +23,7 @@
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/ozone/public/ozone_platform.h"
 
 using testing::_;
 using testing::AllOf;
@@ -103,18 +105,41 @@
   return visibility->data;
 }
 
+::fuchsia::ui::views::ViewRef CloneViewRef(
+    const ::fuchsia::ui::views::ViewRef& view_ref) {
+  ::fuchsia::ui::views::ViewRef dup;
+  zx_status_t status =
+      view_ref.reference.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup.reference);
+  ZX_CHECK(status == ZX_OK, status) << "zx_object_duplicate";
+  return dup;
+}
+
 // Verifies that Frames are initially "hidden", changes to "visible" once the
 // View is attached to a Presenter and back to "hidden" when the View is
 // detached from the Presenter.
-// TODO(https://crbug.com/1314086): Re-enable this test when we can make it work
-// with the fake hardware display controller provider used for tests.
-IN_PROC_BROWSER_TEST_F(FrameImplTest, DISABLED_VisibilityState) {
+// TODO(crbug.com/1377994): Re-enable this test on Arm64 when femu is available
+// for that architecture. This test requires Vulkan and Scenic to properly
+// signal the Views visibility.
+#if defined(ARCH_CPU_ARM_FAMILY)
+#define MAYBE_VisibilityState DISABLED_VisibilityState
+#else
+#define MAYBE_VisibilityState VisibilityState
+#endif
+IN_PROC_BROWSER_TEST_F(FrameImplTest, MAYBE_VisibilityState) {
+  // This test uses the `fuchsia.ui.gfx` variant of `Frame.CreateView*()`.
+  ASSERT_EQ(ui::OzonePlatform::GetInstance()->GetPlatformNameForTest(),
+            "scenic");
+
   net::test_server::EmbeddedTestServerHandle test_server_handle;
   ASSERT_TRUE(test_server_handle =
                   embedded_test_server()->StartAndReturnHandle());
   GURL page_url(embedded_test_server()->GetURL(kVisibilityPath));
 
   auto frame = FrameForTest::Create(context(), {});
+  frame.ptr().set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status);
+    ADD_FAILURE() << "Frame disconnected.";
+  });
   base::RunLoop().RunUntilIdle();
 
   // Navigate to a page and wait for it to finish loading.
@@ -129,25 +154,35 @@
   // Query the document.visibilityState after creating the View, but without it
   // actually "attached" to the view tree.
   auto view_tokens = scenic::ViewTokenPair::New();
-  frame->CreateView(std::move(view_tokens.view_token));
+  auto view_ref_pair = scenic::ViewRefPair::New();
+  frame->CreateViewWithViewRef(std::move(view_tokens.view_token),
+                               std::move(view_ref_pair.control_ref),
+                               CloneViewRef(view_ref_pair.view_ref));
+
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetDocumentVisibilityState(frame.ptr().get()), "\"hidden\"");
 
   // Attach the View to a Presenter, the page should be visible.
   auto presenter = base::ComponentContextForProcess()
                        ->svc()
-                       ->Connect<::fuchsia::ui::policy::Presenter>();
-  presenter.set_error_handler(
-      [](zx_status_t) { ADD_FAILURE() << "Presenter disconnected."; });
-  presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
-                                  nullptr);
+                       ->Connect<::fuchsia::element::GraphicalPresenter>();
+  presenter.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status) << "GraphicalPresenter disconnected.";
+    ADD_FAILURE();
+  });
+  ::fuchsia::element::ViewSpec view_spec;
+  view_spec.set_view_holder_token(std::move(view_tokens.view_holder_token));
+  view_spec.set_view_ref(std::move(view_ref_pair.view_ref));
+  ::fuchsia::element::ViewControllerPtr view_controller;
+  presenter->PresentView(std::move(view_spec), nullptr,
+                         view_controller.NewRequest(),
+                         [](auto result) { EXPECT_FALSE(result.is_err()); });
   frame.navigation_listener().RunUntilTitleEquals("visible");
 
-  // Attach a new View to the Presenter, the page should be hidden again.
-  // This part of the test is a regression test for crbug.com/1141093.
-  auto view_tokens2 = scenic::ViewTokenPair::New();
-  presenter->PresentOrReplaceView(std::move(view_tokens2.view_holder_token),
-                                  nullptr);
+  // Detach the ViewController, causing the View to be detached.
+  // This is a regression test for crbug.com/1141093, verifying that the page
+  // receives a "not visible" event as a result.
+  view_controller->Dismiss();
   frame.navigation_listener().RunUntilTitleEquals("hidden");
 }
 
@@ -916,19 +951,34 @@
 #define MAYBE_SetPageScale DISABLED_SetPageScale
 #endif
 IN_PROC_BROWSER_TEST_F(FrameImplTest, MAYBE_SetPageScale) {
+  // This test uses the `fuchsia.ui.gfx` variant of `Frame.CreateView*()`.
+  ASSERT_EQ(ui::OzonePlatform::GetInstance()->GetPlatformNameForTest(),
+            "scenic");
+
   auto frame = FrameForTest::Create(context(), {});
 
   auto view_tokens = scenic::ViewTokenPair::New();
-  frame->CreateView(std::move(view_tokens.view_token));
+  auto view_ref_pair = scenic::ViewRefPair::New();
+  frame->CreateViewWithViewRef(std::move(view_tokens.view_token),
+                               std::move(view_ref_pair.control_ref),
+                               CloneViewRef(view_ref_pair.view_ref));
 
   // Attach the View to a Presenter, the page should be visible.
   auto presenter = base::ComponentContextForProcess()
                        ->svc()
-                       ->Connect<::fuchsia::ui::policy::Presenter>();
-  presenter.set_error_handler(
-      [](zx_status_t) { ADD_FAILURE() << "Presenter disconnected."; });
-  presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
-                                  nullptr);
+                       ->Connect<::fuchsia::element::GraphicalPresenter>();
+  presenter.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status) << "GraphicalPresenter disconnected.";
+    ADD_FAILURE();
+  });
+
+  ::fuchsia::element::ViewSpec view_spec;
+  view_spec.set_view_holder_token(std::move(view_tokens.view_holder_token));
+  view_spec.set_view_ref(std::move(view_ref_pair.view_ref));
+  ::fuchsia::element::ViewControllerPtr view_controller;
+  presenter->PresentView(std::move(view_spec), nullptr,
+                         view_controller.NewRequest(),
+                         [](auto result) { EXPECT_FALSE(result.is_err()); });
 
   net::test_server::EmbeddedTestServerHandle test_server_handle;
   ASSERT_TRUE(test_server_handle =
@@ -996,10 +1046,15 @@
   auto frame2 = FrameForTest::Create(context(), {});
 
   view_tokens = scenic::ViewTokenPair::New();
-  frame2->CreateView(std::move(view_tokens.view_token));
+  view_ref_pair = scenic::ViewRefPair::New();
+  frame->CreateViewWithViewRef(std::move(view_tokens.view_token),
+                               std::move(view_ref_pair.control_ref),
+                               CloneViewRef(view_ref_pair.view_ref));
 
-  presenter->PresentOrReplaceView(std::move(view_tokens.view_holder_token),
-                                  nullptr);
+  view_spec.set_view_holder_token(std::move(view_tokens.view_holder_token));
+  view_spec.set_view_ref(std::move(view_ref_pair.view_ref));
+  presenter->PresentView(std::move(view_spec), nullptr,
+                         view_controller.NewRequest(), [](auto) {});
 
   EXPECT_TRUE(LoadUrlAndExpectResponse(frame2.GetNavigationController(),
                                        fuchsia::web::LoadUrlParams(),
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index 211533d4..883452df 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -188,49 +188,6 @@
     default;
 #endif
 
-D3DImageBacking::SignaledFence::SignaledFence(
-    scoped_refptr<D3DSharedFence> fence,
-    uint64_t value)
-    : fence_(std::move(fence)), value_(value) {
-  DCHECK(fence_);
-  DCHECK_NE(value_, UINT64_MAX);
-}
-D3DImageBacking::SignaledFence::SignaledFence(const SignaledFence&) = default;
-D3DImageBacking::SignaledFence& D3DImageBacking::SignaledFence::operator=(
-    const SignaledFence&) = default;
-D3DImageBacking::SignaledFence::~SignaledFence() = default;
-
-bool D3DImageBacking::SignaledFence::WaitD3D11(
-    Microsoft::WRL::ComPtr<ID3D11Device> device) const {
-  // D3DSharedFence::WaitD3D11 skips the wait if the device created this fence.
-  return fence_->WaitD3D11(std::move(device), value_);
-}
-
-bool D3DImageBacking::SignaledFence::IncrementAndSignalD3D11() {
-  if (!fence_->SignalD3D11(value_ + 1)) {
-    DLOG(ERROR) << "D3DSharedFence::Signal failed";
-    return false;
-  }
-  value_++;
-  return true;
-}
-
-bool D3DImageBacking::SignaledFence::Update(const SignaledFence& other) {
-  if (fence_ == other.fence_) {
-    if (value_ < other.value_ && other.value_ != UINT64_MAX)
-      value_ = other.value_;
-    return true;
-  }
-  return false;
-}
-
-#if BUILDFLAG(USE_DAWN)
-ExternalImageDXGIFenceDescriptor
-D3DImageBacking::SignaledFence::AsDawnFenceDescriptor() const {
-  return ExternalImageDXGIFenceDescriptor{fence_->GetSharedHandle(), value_};
-}
-#endif
-
 // static
 std::unique_ptr<D3DImageBacking> D3DImageBacking::CreateFromSwapChainBuffer(
     const Mailbox& mailbox,
@@ -718,12 +675,11 @@
 
   // Create the D3D11 device fence on first Dawn access.
   if (!dxgi_shared_handle_state_->has_keyed_mutex() && !d3d11_device_fence_) {
-    auto fence = D3DSharedFence::CreateForD3D11(d3d11_device_);
-    if (!fence) {
+    d3d11_device_fence_ = D3DSharedFence::CreateForD3D11(d3d11_device_);
+    if (!d3d11_device_fence_) {
       DLOG(ERROR) << "Failed to create D3D11 signal fence";
       return nullptr;
     }
-    d3d11_device_fence_ = SignaledFence(std::move(fence), 0);
     // Make D3D11 device wait for |write_fence_| since we'll replace it below.
     if (write_fence_ && !write_fence_->WaitD3D11(d3d11_device_)) {
       DLOG(ERROR) << "Failed to wait for write fence";
@@ -746,14 +702,17 @@
   std::vector<ExternalImageDXGIFenceDescriptor> wait_fences;
   // Always wait for previous write for both read-only or read-write access.
   // Skip the wait if it's for the fence last signaled by the device.
-  if (write_fence_ && write_fence_->fence() != dawn_signaled_fence)
-    wait_fences.push_back(write_fence_->AsDawnFenceDescriptor());
+  if (write_fence_ && write_fence_.get() != dawn_signaled_fence)
+    wait_fences.push_back(ExternalImageDXGIFenceDescriptor{
+        write_fence_->GetSharedHandle(), write_fence_->GetFenceValue()});
   // Also wait for all previous reads for read-write access.
   if (write_access) {
     for (const auto& read_fence : read_fences_) {
       // Skip the wait if it's for the fence last signaled by the device.
-      if (read_fence.fence() != dawn_signaled_fence)
-        wait_fences.push_back(read_fence.AsDawnFenceDescriptor());
+      if (read_fence != dawn_signaled_fence) {
+        wait_fences.push_back(ExternalImageDXGIFenceDescriptor{
+            read_fence->GetSharedHandle(), read_fence->GetFenceValue()});
+      }
     }
   }
 
@@ -803,7 +762,7 @@
       ExternalImageDXGIFenceDescriptor descriptor;
       external_image->EndAccess(texture, &descriptor);
 
-      absl::optional<SignaledFence> signaled_fence;
+      scoped_refptr<D3DSharedFence> signaled_fence;
       if (descriptor.fenceHandle != nullptr) {
         scoped_refptr<D3DSharedFence>& cached_fence = it->second.signaled_fence;
         // Try to reuse the last signaled fence if it's the same fence.
@@ -813,12 +772,13 @@
               D3DSharedFence::CreateFromHandle(descriptor.fenceHandle);
           DCHECK(cached_fence);
         }
-        signaled_fence = SignaledFence(cached_fence, descriptor.fenceValue);
+        cached_fence->Update(descriptor.fenceValue);
+        signaled_fence = cached_fence;
       }
       // Dawn should be using either keyed mutex or fence synchronization.
       DCHECK((dxgi_shared_handle_state_ &&
               dxgi_shared_handle_state_->has_keyed_mutex()) ||
-             signaled_fence.has_value());
+             signaled_fence);
       EndAccessCommon(std::move(signaled_fence));
     } else {
       // Erase from cache if external image is invalid i.e. device was lost.
@@ -848,7 +808,7 @@
   if (write_access) {
     // For read-write access, wait for all previous reads, and reset fences.
     for (const auto& fence : read_fences_) {
-      if (!fence.WaitD3D11(d3d11_device_)) {
+      if (!fence->WaitD3D11(d3d11_device_)) {
         DLOG(ERROR) << "Failed to wait for read fence";
         return false;
       }
@@ -870,7 +830,7 @@
   // If D3D11 device signaling fence is present, shared handle should be too.
   DCHECK(!d3d11_device_fence_ || dxgi_shared_handle_state_);
 
-  absl::optional<SignaledFence> signaled_fence;
+  scoped_refptr<D3DSharedFence> signaled_fence;
   if (d3d11_device_fence_ && d3d11_device_fence_->IncrementAndSignalD3D11())
     signaled_fence = d3d11_device_fence_;
 
@@ -893,26 +853,16 @@
 }
 
 void D3DImageBacking::EndAccessCommon(
-    absl::optional<SignaledFence> signaled_fence) {
+    scoped_refptr<D3DSharedFence> signaled_fence) {
   if (in_write_access_) {
-    DCHECK(!write_fence_.has_value());
+    DCHECK(!write_fence_);
     DCHECK(read_fences_.empty());
     in_write_access_ = false;
-    if (signaled_fence)
-      write_fence_.emplace(*signaled_fence);
+    write_fence_ = std::move(signaled_fence);
   } else {
     num_readers_--;
-    if (signaled_fence) {
-      // First try to update an existing read fence, otherwise insert a new one.
-      for (auto& fence : read_fences_) {
-        if (fence.Update(*signaled_fence)) {
-          signaled_fence.reset();
-          break;
-        }
-      }
-      if (signaled_fence)
-        read_fences_.emplace_back(*signaled_fence);
-    }
+    if (signaled_fence)
+      read_fences_.insert(std::move(signaled_fence));
   }
 }
 
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.h b/gpu/command_buffer/service/shared_image/d3d_image_backing.h
index 50c5f48..3210a82 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.h
@@ -154,42 +154,6 @@
       scoped_refptr<SharedContextState> context_state) override;
 
  private:
-  // A simple wrapper around a D3DSharedFence and a value that was previously
-  // signaled on the fence. This supports copy semantics so that it can be
-  // stored in |read_fences_| or |write_fence_|.
-  class SignaledFence {
-   public:
-    SignaledFence(scoped_refptr<D3DSharedFence> fence, uint64_t value);
-    SignaledFence(const SignaledFence&);
-    SignaledFence& operator=(const SignaledFence&);
-    ~SignaledFence();
-
-    const D3DSharedFence* fence() const { return fence_.get(); }
-
-    // Issues a wait on the |fence_| using |value_|. Wait is skipped if this
-    // fence is signaled by |device|. Returns true on success.
-    bool WaitD3D11(Microsoft::WRL::ComPtr<ID3D11Device> device) const;
-
-    // Issues a signal for the |fence_| with |value_| + 1 on the D3D11 device
-    // this fence was created with, and increments |value_| only on success.
-    // Returns true on success.
-    bool IncrementAndSignalD3D11();
-
-    // Update fence value from |other| if the underlying fence is the same.
-    // Returns true if the underlying fence was the same and either the value of
-    // this fence was updated to the new value or retained because it was ahead.
-    bool Update(const SignaledFence& other);
-
-#if BUILDFLAG(USE_DAWN)
-    // Wrap shared handle for |fence_| and |value_| in a Dawn fence descriptor.
-    ExternalImageDXGIFenceDescriptor AsDawnFenceDescriptor() const;
-#endif
-
-   private:
-    scoped_refptr<D3DSharedFence> fence_;
-    uint64_t value_ = 0;
-  };
-
 #if BUILDFLAG(USE_DAWN)
   struct DawnExternalImageState {
     DawnExternalImageState();
@@ -234,7 +198,7 @@
 
   bool ValidateBeginAccess(bool write_access) const;
 
-  void EndAccessCommon(absl::optional<SignaledFence> signaled_fence);
+  void EndAccessCommon(scoped_refptr<D3DSharedFence> fence);
 
   // Texture could be nullptr if an empty backing is needed for testing.
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture_;
@@ -278,16 +242,16 @@
 
   // Fences for previous reads. These will be waited on by the subsequent write,
   // but not by reads.
-  std::vector<SignaledFence> read_fences_;
+  base::flat_set<scoped_refptr<D3DSharedFence>> read_fences_;
 
   // Fence for the previous write. These will be waited on by subsequent reads
   // and/or write.
-  absl::optional<SignaledFence> write_fence_;
+  scoped_refptr<D3DSharedFence> write_fence_;
 
   // Fence used for signaling on this backing's |d3d11_device_|. Lazily created
   // and signaled on first Dawn access, and used on any subsequent D3D11 access.
   // TODO(sunnyps): Support multiple D3D11 devices.
-  absl::optional<SignaledFence> d3d11_device_fence_;
+  scoped_refptr<D3DSharedFence> d3d11_device_fence_;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/d3d_shared_fence.cc b/gpu/command_buffer/service/shared_image/d3d_shared_fence.cc
index 175fce74..644028b 100644
--- a/gpu/command_buffer/service/shared_image/d3d_shared_fence.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_shared_fence.cc
@@ -93,6 +93,10 @@
   return shared_handle_.get();
 }
 
+uint64_t D3DSharedFence::GetFenceValue() const {
+  return fence_value_;
+}
+
 bool D3DSharedFence::IsSameFenceAsHandle(HANDLE shared_handle) const {
   using PFN_COMPARE_OBJECT_HANDLES =
       BOOL(WINAPI*)(HANDLE hFirstObjectHandle, HANDLE hSecondObjectHandle);
@@ -118,9 +122,13 @@
   return false;
 }
 
+void D3DSharedFence::Update(uint64_t fence_value) {
+  if (fence_value > fence_value_)
+    fence_value_ = fence_value;
+}
+
 bool D3DSharedFence::WaitD3D11(
-    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
-    uint64_t wait_value) {
+    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device) {
   // Skip wait if passed in device is the same as signaling device.
   if (d3d11_device == d3d11_device_)
     return true;
@@ -149,7 +157,7 @@
     return false;
 
   const Microsoft::WRL::ComPtr<ID3D11Fence>& fence = it->second;
-  HRESULT hr = context4->Wait(fence.Get(), wait_value);
+  HRESULT hr = context4->Wait(fence.Get(), fence_value_);
   if (FAILED(hr)) {
     DLOG(ERROR) << "D3D11 fence wait failed: 0x" << std::hex << hr;
     return false;
@@ -157,7 +165,7 @@
   return true;
 }
 
-bool D3DSharedFence::SignalD3D11(uint64_t signal_value) {
+bool D3DSharedFence::IncrementAndSignalD3D11() {
   DCHECK(d3d11_device_);
   DCHECK(d3d11_signal_fence_);
 
@@ -166,11 +174,12 @@
   if (!context4)
     return false;
 
-  HRESULT hr = context4->Signal(d3d11_signal_fence_.Get(), signal_value);
+  HRESULT hr = context4->Signal(d3d11_signal_fence_.Get(), fence_value_ + 1);
   if (FAILED(hr)) {
     DLOG(ERROR) << "D3D11 fence signal failed: 0x" << std::hex << hr;
     return false;
   }
+  fence_value_++;
   return true;
 }
 
diff --git a/gpu/command_buffer/service/shared_image/d3d_shared_fence.h b/gpu/command_buffer/service/shared_image/d3d_shared_fence.h
index 90a6042e..fbd924a 100644
--- a/gpu/command_buffer/service/shared_image/d3d_shared_fence.h
+++ b/gpu/command_buffer/service/shared_image/d3d_shared_fence.h
@@ -23,7 +23,9 @@
 class GPU_GLES2_EXPORT D3DSharedFence
     : public base::RefCounted<D3DSharedFence> {
  public:
-  // Create a new ID3D11Fence with initial value 0 on given |d3d11_device|.
+  // Create a new ID3D11Fence with initial value 0 on given |d3d11_device|. The
+  // provided device is considered the owning device for the fence, and is the
+  // device used for signaling the fence.
   static scoped_refptr<D3DSharedFence> CreateForD3D11(
       Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
 
@@ -38,18 +40,24 @@
   // Return the shared handle for the fence.
   HANDLE GetSharedHandle() const;
 
+  // Returns the fence value that was last known to be signaled on this fence,
+  // and should be used for waiting.
+  uint64_t GetFenceValue() const;
+
   // Returns true if the underlying fence object is the same as |shared_handle|.
   bool IsSameFenceAsHandle(HANDLE shared_handle) const;
 
+  // Updates fence value to |fence_value| provided that it's larger.
+  void Update(uint64_t fence_value);
+
   // Issue a wait for the fence on the immediate context of |d3d11_device| using
   // |wait_value|. The wait is skipped if the passed in device is the same as
   // |d3d11_device_|. Returns true on success.
-  bool WaitD3D11(Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
-                 uint64_t wait_value);
+  bool WaitD3D11(Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
 
   // Issue a signal for the fence on the immediate context of |d3d11_device_|
   // using |signal_value|. Returns true on success.
-  bool SignalD3D11(uint64_t signal_value);
+  bool IncrementAndSignalD3D11();
 
  private:
   friend class base::RefCounted<D3DSharedFence>;
@@ -63,6 +71,9 @@
   // Owned shared handle corresponding to the fence.
   base::win::ScopedHandle shared_handle_;
 
+  // Value last known to be signaled for this fence to be used for future waits.
+  uint64_t fence_value_ = 0;
+
   // If present, this is the D3D11 device that the fence was created on, and
   // used to signal |d3d11_fence_|.
   Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
diff --git a/infra/config/console-header.star b/infra/config/console-header.star
index 1f874250..14cf612f 100644
--- a/infra/config/console-header.star
+++ b/infra/config/console-header.star
@@ -89,9 +89,9 @@
                     alt = "Chrome perf dashboard",
                 ),
                 headers.link(
-                    text = "flake-portal",
+                    text = "LUCI Analysis",
                     branch_selector = branches.ALL_BRANCHES,
-                    url = "https://analysis.chromium.org/p/chromium/flake-portal",
+                    url = "https://luci-analysis.appspot.com",
                     alt = "New flake portal",
                 ),
                 headers.link(
diff --git a/infra/config/dev/chromium-header.textpb b/infra/config/dev/chromium-header.textpb
index 5dfa835..0b0bb6de 100644
--- a/infra/config/dev/chromium-header.textpb
+++ b/infra/config/dev/chromium-header.textpb
@@ -30,8 +30,8 @@
     alt: "Chrome perf dashboard"
   }
   links {
-    text: "flake-portal"
-    url: "https://analysis.chromium.org/p/chromium/flake-portal"
+    text: "LUCI Analysis"
+    url: "https://luci-analysis.appspot.com"
     alt: "New flake portal"
   }
   links {
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-dbg/properties.json b/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-dbg/properties.json
deleted file mode 100644
index 7aab3194..0000000
--- a/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-dbg/properties.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "$build/goma": {
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.mac",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux_chromium_archive_rel_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_archive_rel_ng/properties.json
index 3482017..5ca2ae5 100644
--- a/infra/config/generated/builders/try/linux_chromium_archive_rel_ng/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_archive_rel_ng/properties.json
@@ -37,11 +37,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_chromium_cfi_rel_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_cfi_rel_ng/properties.json
index 7650049..25cae13 100644
--- a/infra/config/generated/builders/try/linux_chromium_cfi_rel_ng/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_cfi_rel_ng/properties.json
@@ -37,11 +37,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/properties.json
index db608376..c7a9e89 100644
--- a/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/properties.json
@@ -79,11 +79,6 @@
       "is_compile_only": true
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_chromium_dbg_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_dbg_ng/properties.json
index 676301d..6e494d7c 100644
--- a/infra/config/generated/builders/try/linux_chromium_dbg_ng/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_dbg_ng/properties.json
@@ -72,11 +72,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json b/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json
index 2500aa1c..937cf91 100644
--- a/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_msan_focal/properties.json
@@ -35,12 +35,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "jobs": 150,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_chromium_msan_rel_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_msan_rel_ng/properties.json
index 512e1d8..90b4eeb 100644
--- a/infra/config/generated/builders/try/linux_chromium_msan_rel_ng/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_msan_rel_ng/properties.json
@@ -72,12 +72,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "jobs": 150,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_chromium_ubsan_rel_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_ubsan_rel_ng/properties.json
index 0fe47da..3f61a56 100644
--- a/infra/config/generated/builders/try/linux_chromium_ubsan_rel_ng/properties.json
+++ b/infra/config/generated/builders/try/linux_chromium_ubsan_rel_ng/properties.json
@@ -37,11 +37,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/builders/try/linux_vr/properties.json b/infra/config/generated/builders/try/linux_vr/properties.json
index f73afafc..1e5aaf0 100644
--- a/infra/config/generated/builders/try/linux_vr/properties.json
+++ b/infra/config/generated/builders/try/linux_vr/properties.json
@@ -40,11 +40,6 @@
       ]
     }
   },
-  "$build/goma": {
-    "enable_ats": true,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org"
-  },
   "$build/reclient": {
     "instance": "rbe-chromium-untrusted",
     "metrics_project": "chromium-reclient-metrics"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index c02b15c..6bf5a405 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -513,9 +513,6 @@
 * [linux-rel-ml](https://ci.chromium.org/p/chromium/builders/try/linux-rel-ml) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux-rel-ml"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""linux-rel-ml""))
   * Experiment percentage: 5.0
 
-* [mac-rel-inverse-fyi](https://ci.chromium.org/p/chromium/builders/try/mac-rel-inverse-fyi) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""mac-rel-inverse-fyi"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""mac-rel-inverse-fyi""))
-  * Experiment percentage: 100.0
-
 * [mac12-arm64-rel](https://ci.chromium.org/p/chromium/builders/try/mac12-arm64-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""mac12-arm64-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""mac12-arm64-rel""))
   * Experiment percentage: 100.0
 
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 71ab097..c856372 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1968,10 +1968,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/gpu-fyi-try-mac-nvidia-retina-dbg"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/gpu-fyi-try-mac-nvidia-retina-exp"
         includable_only: true
       }
@@ -3332,24 +3328,7 @@
       }
       builders {
         name: "chromium/try/mac-rel-inverse-fyi"
-        experiment_percentage: 100
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "docs/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/generated/builders/try/mac-rel-inverse-fyi/.+"
-        }
+        includable_only: true
       }
       builders {
         name: "chromium/try/mac-swangle-chromium-try-x64"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 0f18198..4b6e4a61 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -38086,7 +38086,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69386,111 +69386,6 @@
       }
     }
     builders {
-      name: "gpu-fyi-try-mac-nvidia-retina-dbg"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.gpu.mac.retina.nvidia.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/gpu-fyi-try-mac-nvidia-retina-dbg/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: 21600
-      expiration_secs: 7200
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      build_numbers: YES
-      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.omit_python2"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        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/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "gpu-fyi-try-mac-nvidia-retina-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -75529,7 +75424,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85272,6 +85167,10 @@
         value: 5
       }
       experiments {
+        key: "chromium_rts.inverted_rts"
+        value: 100
+      }
+      experiments {
         key: "chromium_swarming.expose_merge_script_failures"
         value: 100
       }
diff --git a/infra/config/generated/luci/luci-milo-dev.cfg b/infra/config/generated/luci/luci-milo-dev.cfg
index bfb9c5f..481ef89 100644
--- a/infra/config/generated/luci/luci-milo-dev.cfg
+++ b/infra/config/generated/luci/luci-milo-dev.cfg
@@ -62,8 +62,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index f88b9b2..dde9bc6 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -716,8 +716,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -1461,8 +1461,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -1965,8 +1965,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -2324,8 +2324,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -2947,8 +2947,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -3391,8 +3391,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -3731,8 +3731,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -4294,8 +4294,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -4682,8 +4682,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -5091,8 +5091,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -5538,8 +5538,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -6102,8 +6102,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -6581,8 +6581,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -6962,8 +6962,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -7341,8 +7341,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -7834,8 +7834,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -8705,8 +8705,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -9136,8 +9136,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -9541,8 +9541,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -9936,8 +9936,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -10593,8 +10593,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -11007,8 +11007,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -11426,8 +11426,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -11880,8 +11880,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -12244,8 +12244,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -12698,8 +12698,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -13053,8 +13053,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -13432,8 +13432,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -13916,8 +13916,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -14290,8 +14290,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -14705,8 +14705,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -15099,8 +15099,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -15504,8 +15504,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -15856,8 +15856,8 @@
         alt: "Chrome perf dashboard"
       }
       links {
-        text: "flake-portal"
-        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        text: "LUCI Analysis"
+        url: "https://luci-analysis.appspot.com"
         alt: "New flake portal"
       }
       links {
@@ -16532,9 +16532,6 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-intel-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-nvidia-retina-dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-nvidia-retina-exp"
   }
   builders {
@@ -18015,9 +18012,6 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-intel-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-nvidia-retina-dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-mac-nvidia-retina-exp"
   }
   builders {
diff --git a/infra/config/subprojects/chromium/ci/chromium.accessibility.star b/infra/config/subprojects/chromium/ci/chromium.accessibility.star
index 72d185b5..7dbdeba 100644
--- a/infra/config/subprojects/chromium/ci/chromium.accessibility.star
+++ b/infra/config/subprojects/chromium/ci/chromium.accessibility.star
@@ -19,9 +19,6 @@
     reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
     reclient_instance = reclient.instance.DEFAULT_TRUSTED,
     service_account = ci.DEFAULT_SERVICE_ACCOUNT,
-
-    # TODO(crbug.com/1362440): remove this.
-    omit_python2 = False,
 )
 
 consoles.console_view(
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index e680bbe..55a593c 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -318,11 +318,6 @@
 )
 
 gpu_mac_builder(
-    name = "gpu-fyi-try-mac-nvidia-retina-dbg",
-    pool = "luci.chromium.gpu.mac.retina.nvidia.try",
-)
-
-gpu_mac_builder(
     name = "gpu-fyi-try-mac-nvidia-retina-exp",
     # This bot has one machine backing its tests at the moment.
     # If it gets more, the modified execution_timeout should be removed.
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.accessibility.star b/infra/config/subprojects/chromium/try/tryserver.chromium.accessibility.star
index 1728e0a..c2092ef6 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.accessibility.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.accessibility.star
@@ -18,9 +18,6 @@
     os = os.LINUX_DEFAULT,
     pool = try_.DEFAULT_POOL,
     service_account = try_.DEFAULT_SERVICE_ACCOUNT,
-
-    # TODO(crbug.com/1362440): remove this.
-    omit_python2 = False,
 )
 
 consoles.list_view(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index 14b05cb..4f2e8e8 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -339,6 +339,7 @@
     mirrors = [
         "ci/linux-archive-rel",
     ],
+    goma_backend = None,
 )
 
 try_.orchestrator_builder(
@@ -375,6 +376,7 @@
     cores = 32,
     # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
     execution_timeout = 7 * time.hour,
+    goma_backend = None,
 )
 
 try_.builder(
@@ -457,6 +459,7 @@
         include_all_triggered_testers = True,
         is_compile_only = True,
     ),
+    goma_backend = None,
 )
 
 try_.builder(
@@ -478,6 +481,7 @@
             "build/.*check_gn_headers.*",
         ],
     ),
+    goma_backend = None,
 )
 
 try_.builder(
@@ -486,7 +490,7 @@
         "ci/Linux MSan Focal",
     ],
     execution_timeout = 16 * time.hour,
-    goma_jobs = goma.jobs.J150,
+    goma_backend = None,
     os = os.LINUX_FOCAL,
 )
 
@@ -497,7 +501,7 @@
         "ci/Linux MSan Tests",
     ],
     execution_timeout = 6 * time.hour,
-    goma_jobs = goma.jobs.J150,
+    goma_backend = None,
 )
 
 try_.orchestrator_builder(
@@ -531,6 +535,7 @@
     mirrors = [
         "ci/linux-ubsan-vptr",
     ],
+    goma_backend = None,
 )
 
 try_.builder(
@@ -601,6 +606,7 @@
             "content/browser/xr/.+",
         ],
     ),
+    goma_backend = None,
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index 45e7f0cb..286a862 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -110,6 +110,9 @@
     use_clang_coverage = True,
     coverage_test_types = ["overall", "unit"],
     tryjob = try_.job(),
+    experiments = {
+        "chromium_rts.inverted_rts": 100,
+    },
     # TODO (crbug.com/1372179): Use orchestrator pool once overloaded test pools
     # are addressed
     #use_orchestrator_pool = True,
@@ -128,9 +131,6 @@
     main_list_view = "try",
     use_clang_coverage = True,
     coverage_test_types = ["overall", "unit"],
-    tryjob = try_.job(
-        experiment_percentage = 100,
-    ),
     experiments = {
         "chromium_rts.inverted_rts": 100,
         "chromium_rts.inverted_rts_bail_early": 100,
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index 51f7a46..1e514a0b 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -673,8 +673,6 @@
       self.feedWrapperViewController;
   self.ntpViewController.overscrollDelegate = self;
   self.ntpViewController.ntpContentDelegate = self;
-  self.ntpViewController.identityDiscButton =
-      [self.headerController identityDiscButton];
 
   self.ntpViewController.headerController = self.headerController;
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
index 06264c3..a612ff4 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
@@ -46,11 +46,7 @@
 // controller.
 @property(nonatomic, weak) ViewRevealingVerticalPanHandler* panGestureHandler;
 
-// Identity disc shown in the NTP.
-// TODO(crbug.com/1170995): Remove once the Feed header properly supports
-// ContentSuggestions.
-@property(nonatomic, weak) UIButton* identityDiscButton;
-
+// The view controller representing the content suggestions.
 @property(nonatomic, strong)
     ContentSuggestionsViewController* contentSuggestionsViewController;
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index 2542763d..8c1b5b6 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -86,6 +86,11 @@
 @property(nonatomic, strong)
     NSMutableArray<UIViewController*>* viewControllersAboveFeed;
 
+// Identity disc shown in the NTP.
+// TODO(crbug.com/1170995): Remove once the Feed header properly supports
+// ContentSuggestions.
+@property(nonatomic, weak) UIButton* identityDiscButton;
+
 @end
 
 @implementation NewTabPageViewController
@@ -145,6 +150,9 @@
   [self registerNotifications];
 
   [self layoutContentInParentCollectionView];
+
+  self.identityDiscButton = [self.headerController identityDiscButton];
+  DCHECK(self.identityDiscButton);
 }
 
 - (void)viewWillLayoutSubviews {
@@ -197,10 +205,6 @@
   // changed in another tab. This ensures that the sticky elements are correct
   // whenever an NTP reappears.
   [self handleStickyElementsForScrollPosition:[self scrollPosition] force:YES];
-  
-  if (![self isInitialOffsetFromSavedState]) {
-    [self setContentOffsetToTop];
-  }
 
   self.viewDidAppear = YES;
 }
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/BUILD.gn b/ios/chrome/browser/ui/omnibox/keyboard_assist/BUILD.gn
index bceb414..ade75cc 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/BUILD.gn
@@ -28,6 +28,7 @@
     "resources:keyboard_accessory_voice_search",
     "//base",
     "//base:i18n",
+    "//components/search_engines",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/flags:system_flags",
     "//ios/chrome/browser/search_engines:search_engines",
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h
index f5656d3f..f5cf985cf 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h
@@ -13,7 +13,6 @@
 @protocol LensCommands;
 @class OmniboxTextFieldIOS;
 @protocol QRScannerCommands;
-class TemplateURLService;
 
 // Delegate protocol for the KeyboardAccessoryView.
 @protocol OmniboxAssistiveKeyboardDelegate
@@ -21,10 +20,6 @@
 // The layout guide center for the current scene.
 @property(nonatomic, strong) LayoutGuideCenter* layoutGuideCenter;
 
-// The templateURLService used by this mediator to extract whether the default
-// search engine is Google.
-@property(nonatomic, assign) TemplateURLService* templateURLService;
-
 // Notifies the delegate that the Voice Search button was tapped.
 - (void)keyboardAccessoryVoiceSearchTapped:(id)sender;
 
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm
index 3e6ec29..715b66b 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.mm
@@ -26,7 +26,6 @@
 
 @synthesize applicationCommandsHandler = _applicationCommandsHandler;
 @synthesize browserCommandsHandler = _browserCommandsHandler;
-@synthesize templateURLService = _templateURLService;
 @synthesize layoutGuideCenter = _layoutGuideCenter;
 @synthesize qrScannerCommandsHandler = _qrScannerCommandsHandler;
 @synthesize omniboxTextField = _omniboxTextField;
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h
index 3fd2c21..4b45d038 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h
@@ -8,18 +8,25 @@
 #import <UIKit/UIKit.h>
 
 @protocol OmniboxAssistiveKeyboardDelegate;
+@class OmniboxKeyboardAccessoryView;
+class TemplateURLService;
 
 // Adds a keyboard assistive view [1] to `textField`. The assistive view
 // contains among other things a button to quickly enter `dotComTLD`, and the
 // callbacks are handled via `delegate`. `dotComTLD` must not be nil.
+// `templateURLService' must be so the keyboard can be keep track of the default
+// search engine.
 //
 // [1]
 // On iPhone the assistive view is a keyboard accessory view.
 // On iPad, the assitive view is an inputAssistantItem to handle split
 // keyboards.
-void ConfigureAssistiveKeyboardViews(
+//
+// Returns the keyboard accessory view if on iPhone, otherwise returns nil.
+OmniboxKeyboardAccessoryView* ConfigureAssistiveKeyboardViews(
     UITextField* textField,
     NSString* dotComTLD,
-    id<OmniboxAssistiveKeyboardDelegate> delegate);
+    id<OmniboxAssistiveKeyboardDelegate> delegate,
+    TemplateURLService* templateURLService);
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_KEYBOARD_ASSIST_OMNIBOX_ASSISTIVE_KEYBOARD_VIEWS_H_
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.mm b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.mm
index 7c2116d..f080f3f 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.mm
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h"
 
 #import "base/check.h"
+#import "components/search_engines/template_url_service.h"
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_input_assistant_items.h"
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h"
@@ -14,10 +15,11 @@
 #error "This file requires ARC support."
 #endif
 
-void ConfigureAssistiveKeyboardViews(
+OmniboxKeyboardAccessoryView* ConfigureAssistiveKeyboardViews(
     UITextField* textField,
     NSString* dotComTLD,
-    id<OmniboxAssistiveKeyboardDelegate> delegate) {
+    id<OmniboxAssistiveKeyboardDelegate> delegate,
+    TemplateURLService* templateURLService) {
   DCHECK(dotComTLD);
   NSArray<NSString*>* buttonTitles = @[ @":", @"-", @"/", dotComTLD ];
 
@@ -29,11 +31,16 @@
   } else {
     textField.inputAssistantItem.leadingBarButtonGroups = @[];
     textField.inputAssistantItem.trailingBarButtonGroups = @[];
-    UIView* keyboardAccessoryView =
-        [[OmniboxKeyboardAccessoryView alloc] initWithButtons:buttonTitles
-                                                     delegate:delegate
-                                                  pasteTarget:textField];
+    OmniboxKeyboardAccessoryView* keyboardAccessoryView =
+        [[OmniboxKeyboardAccessoryView alloc]
+               initWithButtons:buttonTitles
+                      delegate:delegate
+                   pasteTarget:textField
+            templateURLService:templateURLService];
     [keyboardAccessoryView setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
     [textField setInputAccessoryView:keyboardAccessoryView];
+    return keyboardAccessoryView;
   }
+
+  return nil;
 }
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h
index 304e41f..f77de95 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h
@@ -9,6 +9,8 @@
 
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h"
 
+class TemplateURLService;
+
 // Accessory View above the keyboard.
 // Shows keys that are shortcuts to commonly used characters or strings,
 // and buttons to start Voice Search, Camera Search or Paste Search.
@@ -20,6 +22,7 @@
 - (instancetype)initWithButtons:(NSArray<NSString*>*)buttonTitles
                        delegate:(id<OmniboxAssistiveKeyboardDelegate>)delegate
                     pasteTarget:(id<UIPasteConfigurationSupporting>)pasteTarget
+             templateURLService:(TemplateURLService*)templateURLService
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
@@ -27,6 +30,10 @@
 - (instancetype)initWithFrame:(CGRect)frame
                inputViewStyle:(UIInputViewStyle)inputViewStyle NS_UNAVAILABLE;
 
+// The templateURLService used by this view to determine whether or not
+// Google is the default search engine.
+@property(nonatomic, assign) TemplateURLService* templateURLService;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_KEYBOARD_ASSIST_OMNIBOX_KEYBOARD_ACCESSORY_VIEW_H_
diff --git a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.mm b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.mm
index e87bf0c..aa210c77 100644
--- a/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.mm
+++ b/ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.mm
@@ -48,8 +48,8 @@
 
 - (instancetype)initWithButtons:(NSArray<NSString*>*)buttonTitles
                        delegate:(id<OmniboxAssistiveKeyboardDelegate>)delegate
-                    pasteTarget:
-                        (id<UIPasteConfigurationSupporting>)pasteTarget {
+                    pasteTarget:(id<UIPasteConfigurationSupporting>)pasteTarget
+             templateURLService:(TemplateURLService*)templateURLService {
   self = [super initWithFrame:CGRectZero
                inputViewStyle:UIInputViewStyleKeyboard];
   if (self) {
@@ -58,10 +58,8 @@
     _pasteTarget = pasteTarget;
     self.translatesAutoresizingMaskIntoConstraints = NO;
     self.allowsSelfSizing = YES;
+    self.templateURLService = templateURLService;
     [self addSubviews];
-
-    _searchEngineObserver = std::make_unique<SearchEngineObserverBridge>(
-        self, delegate.templateURLService);
   }
   return self;
 }
@@ -101,7 +99,7 @@
   // Voice search, camera/Lens search and paste search.
   BOOL useLens = ios::provider::IsLensSupported() &&
                  base::FeatureList::IsEnabled(kEnableLensInKeyboard) &&
-                 [self isGoogleSearchEngine:_delegate.templateURLService];
+                 [self isGoogleSearchEngine:self.templateURLService];
   NSArray<UIControl*>* leadingControls =
       OmniboxAssistiveKeyboardLeadingControls(_delegate, self.pasteTarget,
                                               useLens);
@@ -175,11 +173,14 @@
   [_delegate keyPressed:[button currentTitle]];
 }
 
-#pragma mark - UIView overrides
+#pragma mark - Setters
 
-- (void)didMoveToSuperview {
-  [super didMoveToSuperview];
-  if (!self.superview) {
+- (void)setTemplateURLService:(TemplateURLService*)templateURLService {
+  _templateURLService = templateURLService;
+  if (_templateURLService) {
+    _searchEngineObserver =
+        std::make_unique<SearchEngineObserverBridge>(self, templateURLService);
+  } else {
     _searchEngineObserver.reset();
   }
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
index a7b58b7..082ae7b7 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
@@ -35,6 +35,7 @@
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views.h"
+#import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_keyboard_accessory_view.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_mediator.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_return_key_forwarding_delegate.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
@@ -77,6 +78,10 @@
 @property(nonatomic, strong)
     ZeroSuggestPrefetchHelper* zeroSuggestPrefetchHelper;
 
+// The keyboard accessory view. Will be nil if the app is running on an iPad.
+@property(nonatomic, strong)
+    OmniboxKeyboardAccessoryView* keyboardAccessoryView;
+
 @end
 
 @implementation OmniboxCoordinator {
@@ -141,14 +146,13 @@
       self.browser->GetCommandDispatcher(), QRScannerCommands);
   self.keyboardDelegate.layoutGuideCenter =
       LayoutGuideCenterForBrowser(self.browser);
-  self.keyboardDelegate.templateURLService = templateURLService;
   // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
   // clean up.
   self.keyboardDelegate.browserCommandsHandler =
       static_cast<id<BrowserCommands>>(self.browser->GetCommandDispatcher());
   self.keyboardDelegate.omniboxTextField = self.textField;
-  ConfigureAssistiveKeyboardViews(self.textField, kDotComTLD,
-                                  self.keyboardDelegate);
+  self.keyboardAccessoryView = ConfigureAssistiveKeyboardViews(
+      self.textField, kDotComTLD, self.keyboardDelegate, templateURLService);
 
   if (base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetching)) {
     self.zeroSuggestPrefetchHelper = [[ZeroSuggestPrefetchHelper alloc]
@@ -164,6 +168,12 @@
   self.editController = nil;
   self.viewController = nil;
   self.mediator.templateURLService = nullptr;  // Unregister the observer.
+  if (self.keyboardAccessoryView) {
+    // Unregister the observer.
+    self.keyboardAccessoryView.templateURLService = nil;
+  }
+
+  self.keyboardAccessoryView = nil;
   self.mediator = nil;
   self.returnDelegate = nil;
   self.zeroSuggestPrefetchHelper = nil;
diff --git a/media/DEPS b/media/DEPS
index 605f574..671ab21 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -22,7 +22,7 @@
   "+third_party/libgav1",
   "+third_party/libvpx",
   "+third_party/libyuv",
-  "+third_party/openh264/src/codec/api/svc",
+  "+third_party/openh264/src/codec/api/wels",
   "+third_party/opus",
   "+third_party/skia",
   "+ui/base/x/x11_user_input_monitor.h",
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 28417a4c..1f909f2 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -590,11 +590,6 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_LINUX)
 
-// Enable VA-API hardware decode acceleration for AV1.
-BASE_FEATURE(kVaapiAV1Decoder,
-             "VaapiAV1Decoder",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enable VA-API hardware low power encoder for all codecs on intel Gen9x gpu.
 BASE_FEATURE(kVaapiLowPowerEncoderGen9x,
              "VaapiLowPowerEncoderGen9x",
@@ -901,6 +896,11 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+// Enable hardware AV1 decoder on ChromeOS.
+BASE_FEATURE(kChromeOSHWAV1Decoder,
+             "ChromeOSHWAV1Decoder",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Enable Variable Bitrate encoding with hardware accelerated encoders on
 // ChromeOS.
 BASE_FEATURE(kChromeOSHWVBREncoding,
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 4c64486..3c172924 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -219,7 +219,6 @@
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kVaapiVideoEncodeLinux);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kVaapiIgnoreDriverChecks);
 #endif  // BUILDFLAG(IS_LINUX)
-MEDIA_EXPORT BASE_DECLARE_FEATURE(kVaapiAV1Decoder);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kVaapiLowPowerEncoderGen9x);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kVaapiEnforceVideoMinMaxResolution);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kVaapiVideoMinResolutionForPerformance);
@@ -256,6 +255,7 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+MEDIA_EXPORT BASE_DECLARE_FEATURE(kChromeOSHWAV1Decoder);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kChromeOSHWVBREncoding);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseChromeOSDirectVideoDecoder);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kLimitConcurrentDecoderInstances);
diff --git a/media/fuchsia/cdm/fuchsia_cdm.h b/media/fuchsia/cdm/fuchsia_cdm.h
index 9eda5e2..075c410c 100644
--- a/media/fuchsia/cdm/fuchsia_cdm.h
+++ b/media/fuchsia/cdm/fuchsia_cdm.h
@@ -106,9 +106,6 @@
 
   void OnNewKey();
 
-  CdmPromiseAdapter promises_;
-  base::flat_map<std::string, std::unique_ptr<CdmSession>> session_map_;
-
   fuchsia::media::drm::ContentDecryptionModulePtr cdm_;
   ReadyCB ready_cb_;
   SessionCallbacks session_callbacks_;
@@ -119,6 +116,12 @@
   std::vector<base::RepeatingClosure> new_key_callbacks_
       GUARDED_BY(new_key_callbacks_lock_);
 
+  CdmPromiseAdapter promises_;
+
+  // CdmSession instances reference `session_callbacks_`, so they must be
+  // deleted before the callbacks.
+  base::flat_map<std::string, std::unique_ptr<CdmSession>> session_map_;
+
   CallbackRegistry<EventCB::RunType> event_callbacks_;
 };
 
diff --git a/media/gpu/chromeos/dmabuf_video_frame_pool.h b/media/gpu/chromeos/dmabuf_video_frame_pool.h
index 51c236b..3860d74 100644
--- a/media/gpu/chromeos/dmabuf_video_frame_pool.h
+++ b/media/gpu/chromeos/dmabuf_video_frame_pool.h
@@ -24,10 +24,10 @@
 class PlatformVideoFramePool;
 
 // Interface for allocating and managing DMA-buf VideoFrame. The client should
-// set a task runner first, and guarantee both GetFrame() and the destructor are
-// executed on this task runner.
-// Note: other public methods might be called at different thread. The
-// implementation must be thread-safe.
+// set a task runner first via set_parent_task_runner(), and guarantee that
+// Initialize(), GetFrame(), GetGpuBufferLayout() and the destructor are
+// executed on this task runner. Note: other public methods might be called at
+// different thread. The implementation must be thread-safe.
 class MEDIA_GPU_EXPORT DmabufVideoFramePool {
  public:
   using DmabufId = const std::vector<base::ScopedFD>*;
@@ -94,6 +94,10 @@
   // |parent_task_runner_| because it may invalidate weak ptrs.
   virtual void ReleaseAllFrames() = 0;
 
+  // Detailed information of the allocated GpuBufferLayout. Only valid after a
+  // successful Initialize() call, otherwise returns absl::nullopt.
+  virtual absl::optional<GpuBufferLayout> GetGpuBufferLayout() = 0;
+
   // Returns true if and only if the pool is a mock pool used for testing.
   virtual bool IsFakeVideoFramePool();
 
diff --git a/media/gpu/chromeos/platform_video_frame_pool.cc b/media/gpu/chromeos/platform_video_frame_pool.cc
index d20f651..86132db 100644
--- a/media/gpu/chromeos/platform_video_frame_pool.cc
+++ b/media/gpu/chromeos/platform_video_frame_pool.cc
@@ -288,6 +288,12 @@
   weak_this_ = weak_this_factory_.GetWeakPtr();
 }
 
+absl::optional<GpuBufferLayout> PlatformVideoFramePool::GetGpuBufferLayout() {
+  DCHECK(parent_task_runner_->RunsTasksInCurrentSequence());
+  base::AutoLock auto_lock(lock_);
+  return frame_layout_;
+}
+
 // static
 void PlatformVideoFramePool::OnFrameReleasedThunk(
     absl::optional<base::WeakPtr<PlatformVideoFramePool>> pool,
diff --git a/media/gpu/chromeos/platform_video_frame_pool.h b/media/gpu/chromeos/platform_video_frame_pool.h
index 9bcfefe..05187c8 100644
--- a/media/gpu/chromeos/platform_video_frame_pool.h
+++ b/media/gpu/chromeos/platform_video_frame_pool.h
@@ -47,7 +47,6 @@
 
   // DmabufVideoFramePool implementation.
   PlatformVideoFramePool* AsPlatformVideoFramePool() override;
-
   CroStatus::Or<GpuBufferLayout> Initialize(const Fourcc& fourcc,
                                             const gfx::Size& coded_size,
                                             const gfx::Rect& visible_rect,
@@ -59,6 +58,7 @@
   bool IsExhausted() override;
   void NotifyWhenFrameAvailable(base::OnceClosure cb) override;
   void ReleaseAllFrames() override;
+  absl::optional<GpuBufferLayout> GetGpuBufferLayout() override;
 
   // Returns the original frame of a wrapped frame. We need this method to
   // determine whether the frame returned by GetFrame() is the same one after
diff --git a/media/gpu/chromeos/vda_video_frame_pool.cc b/media/gpu/chromeos/vda_video_frame_pool.cc
index 0a21435..0b289a7 100644
--- a/media/gpu/chromeos/vda_video_frame_pool.cc
+++ b/media/gpu/chromeos/vda_video_frame_pool.cc
@@ -180,6 +180,12 @@
   // NOREACHED() for now in order to prevent a DCHECK when this occurs.
 }
 
+absl::optional<GpuBufferLayout> VdaVideoFramePool::GetGpuBufferLayout() {
+  DVLOGF(3);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
+  return layout_;
+}
+
 void VdaVideoFramePool::CallFrameAvailableCbIfNeeded() {
   DVLOGF(4);
   DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
diff --git a/media/gpu/chromeos/vda_video_frame_pool.h b/media/gpu/chromeos/vda_video_frame_pool.h
index c2e467dc..e5621e2 100644
--- a/media/gpu/chromeos/vda_video_frame_pool.h
+++ b/media/gpu/chromeos/vda_video_frame_pool.h
@@ -72,6 +72,7 @@
   bool IsExhausted() override;
   void NotifyWhenFrameAvailable(base::OnceClosure cb) override;
   void ReleaseAllFrames() override;
+  absl::optional<GpuBufferLayout> GetGpuBufferLayout() override;
 
  private:
   // Update the layout of the buffers. |vda_| calls this as
diff --git a/media/gpu/chromeos/video_decoder_pipeline_unittest.cc b/media/gpu/chromeos/video_decoder_pipeline_unittest.cc
index ae91002..1e3d674 100644
--- a/media/gpu/chromeos/video_decoder_pipeline_unittest.cc
+++ b/media/gpu/chromeos/video_decoder_pipeline_unittest.cc
@@ -69,6 +69,7 @@
   MOCK_METHOD0(IsExhausted, bool());
   MOCK_METHOD1(NotifyWhenFrameAvailable, void(base::OnceClosure));
   MOCK_METHOD0(ReleaseAllFrames, void());
+  MOCK_METHOD0(GetGpuBufferLayout, absl::optional<GpuBufferLayout>());
 
   bool IsFakeVideoFramePool() override { return true; }
 };
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index c5c9976..00d4acf8 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -46,13 +46,9 @@
   // there is no intersection between the enabled and disabled set.
   std::vector<base::test::FeatureRef> combined_enabled_features(
       enabled_features);
-  combined_enabled_features.push_back(media::kVp9kSVCHWDecoding);
   std::vector<base::test::FeatureRef> combined_disabled_features(
       disabled_features);
 #if BUILDFLAG(USE_VAAPI)
-  // TODO(b/172217032): remove once enabled by default.
-  combined_enabled_features.push_back(media::kVaapiAV1Decoder);
-
   // Disable this feature so that the decoder test can test a
   // resolution which is denied for the sake of performance. See
   // b/171041334.
diff --git a/media/gpu/v4l2/v4l2_video_decoder.cc b/media/gpu/v4l2/v4l2_video_decoder.cc
index 8877fed..e5175af 100644
--- a/media/gpu/v4l2/v4l2_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder.cc
@@ -465,34 +465,25 @@
   // created by VideoFramePool.
   DmabufVideoFramePool* pool = client_->GetVideoFramePool();
   if (pool) {
-    // TODO(andrescj): the call to PickDecoderOutputFormat() should have already
-    // initialized the frame pool, so this call to Initialize() is redundant.
-    // However, we still have to get the GpuBufferLayout to find out the
-    // modifier that we need to give to the driver. We should add a
-    // GetGpuBufferLayout() method to DmabufVideoFramePool to query that without
-    // having to re-initialize the pool.
-    CroStatus::Or<GpuBufferLayout> status_or_layout = pool->Initialize(
-        fourcc, adjusted_size, visible_rect,
-        aspect_ratio_.GetNaturalSize(visible_rect), num_output_frames_,
-        /*use_protected=*/false);
-    if (!status_or_layout.has_value()) {
-      VLOGF(1) << "Failed to setup format to VFPool";
-      return std::move(status_or_layout).error().code();
-    }
-    const GpuBufferLayout layout = std::move(status_or_layout).value();
-    if (layout.size() != adjusted_size) {
-      VLOGF(1) << "The size adjusted by VFPool is different from one "
-               << "adjusted by a video driver. fourcc: " << fourcc.ToString()
-               << ", (video driver v.s. VFPool) " << adjusted_size.ToString()
-               << " != " << layout.size().ToString();
+    absl::optional<GpuBufferLayout> layout = pool->GetGpuBufferLayout();
+    if (!layout.has_value()) {
+      VLOGF(1) << "Failed to get format from VFPool";
       return CroStatus::Codes::kFailedToChangeResolution;
     }
 
-    VLOGF(1) << "buffer modifier: " << std::hex << layout.modifier();
-    if (layout.modifier() &&
-        layout.modifier() != gfx::NativePixmapHandle::kNoModifier) {
+    if (layout->size() != adjusted_size) {
+      VLOGF(1) << "The size adjusted by VFPool is different from one "
+               << "adjusted by a video driver. fourcc: " << fourcc.ToString()
+               << ", (video driver v.s. VFPool) " << adjusted_size.ToString()
+               << " != " << layout->size().ToString();
+      return CroStatus::Codes::kFailedToChangeResolution;
+    }
+
+    VLOGF(1) << "buffer modifier: " << std::hex << layout->modifier();
+    if (layout->modifier() &&
+        layout->modifier() != gfx::NativePixmapHandle::kNoModifier) {
       absl::optional<struct v4l2_format> modifier_format =
-          output_queue_->SetModifierFormat(layout.modifier(), picked_size);
+          output_queue_->SetModifierFormat(layout->modifier(), picked_size);
       if (!modifier_format)
         return CroStatus::Codes::kFailedToChangeResolution;
 
diff --git a/media/gpu/vaapi/vaapi_unittest.cc b/media/gpu/vaapi/vaapi_unittest.cc
index 558f5108..2a89811f 100644
--- a/media/gpu/vaapi/vaapi_unittest.cc
+++ b/media/gpu/vaapi/vaapi_unittest.cc
@@ -122,14 +122,6 @@
              : absl::nullopt;
 }
 
-std::unique_ptr<base::test::ScopedFeatureList> CreateScopedFeatureList() {
-  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list->InitWithFeatures(
-      /*enabled_features=*/{media::kVaapiAV1Decoder},
-      /*disabled_features=*/{});
-  return scoped_feature_list;
-}
-
 unsigned int ToVaRTFormat(uint32_t va_fourcc) {
   switch (va_fourcc) {
     case VA_FOURCC_I420:
@@ -233,11 +225,8 @@
 
 class VaapiTest : public testing::Test {
  public:
-  VaapiTest() : scoped_feature_list_(CreateScopedFeatureList()) {}
+  VaapiTest() = default;
   ~VaapiTest() override = default;
-
- private:
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
 };
 
 std::map<VAProfile, std::vector<VAEntrypoint>> ParseVainfo(
@@ -931,28 +920,21 @@
 
 int main(int argc, char** argv) {
   base::TestSuite test_suite(argc, argv);
-  {
-    // Enables/Disables features during PreSandboxInitialization(). We have to
-    // destruct ScopedFeatureList after it because base::TestSuite::Run()
-    // creates a ScopedFeatureList and multiple concurrent ScopedFeatureLists
-    // are not allowed.
-    auto scoped_feature_list = media::CreateScopedFeatureList();
 
 #if defined(USE_OZONE) && BUILDFLAG(IS_LINUX)
-    // Initialize Ozone so that the VADisplayState can decide if we're running
-    // on top of a platform that can deal with VA-API buffers.
-    // TODO(b/230370976): we may no longer need to initialize Ozone since we
-    // don't use it for buffer allocation.
-    ui::OzonePlatform::InitParams params;
-    params.single_process = true;
-    ui::OzonePlatform::InitializeForUI(params);
-    ui::OzonePlatform::InitializeForGPU(params);
+  // Initialize Ozone so that the VADisplayState can decide if we're running
+  // on top of a platform that can deal with VA-API buffers.
+  // TODO(b/230370976): we may no longer need to initialize Ozone since we
+  // don't use it for buffer allocation.
+  ui::OzonePlatform::InitParams params;
+  params.single_process = true;
+  ui::OzonePlatform::InitializeForUI(params);
+  ui::OzonePlatform::InitializeForGPU(params);
 #endif
 
-    // PreSandboxInitialization() loads and opens the driver, queries its
-    // capabilities and fills in the VASupportedProfiles.
-    media::VaapiWrapper::PreSandboxInitialization();
-  }
+  // PreSandboxInitialization() loads and opens the driver, queries its
+  // capabilities and fills in the VASupportedProfiles.
+  media::VaapiWrapper::PreSandboxInitialization();
 
   return base::LaunchUnitTests(
       argc, argv,
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index cc1c86f..9051c726 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -629,7 +629,7 @@
 bool IsBlockedDriver(VaapiWrapper::CodecMode mode, VAProfile va_profile) {
   if (!IsModeEncoding(mode)) {
     return va_profile == VAProfileAV1Profile0 &&
-           !base::FeatureList::IsEnabled(kVaapiAV1Decoder);
+           !base::FeatureList::IsEnabled(kChromeOSHWAV1Decoder);
   }
 
   // TODO(posciak): Remove once VP8 encoding is to be enabled by default.
diff --git a/media/video/openh264_video_encoder.h b/media/video/openh264_video_encoder.h
index 09f4922..9d70cc31 100644
--- a/media/video/openh264_video_encoder.h
+++ b/media/video/openh264_video_encoder.h
@@ -12,7 +12,7 @@
 #include "media/base/video_encoder.h"
 #include "media/base/video_frame_pool.h"
 #include "media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.h"
-#include "third_party/openh264/src/codec/api/svc/codec_api.h"
+#include "third_party/openh264/src/codec/api/wels/codec_api.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace media {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index de630fff..1b1e66cb 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1257,8 +1257,6 @@
       "base/net_errors_win.cc",
       "base/network_change_notifier_win.cc",
       "base/network_change_notifier_win.h",
-      "base/network_cost_change_notifier_win.cc",
-      "base/network_cost_change_notifier_win.h",
       "base/network_interfaces_win.cc",
       "base/network_interfaces_win.h",
       "base/platform_mime_util_win.cc",
@@ -2174,8 +2172,6 @@
     "test/ssl_test_util.cc",
     "test/ssl_test_util.h",
     "test/test_certificate_data.h",
-    "test/test_connection_cost_observer.cc",
-    "test/test_connection_cost_observer.h",
     "test/test_data_directory.cc",
     "test/test_data_directory.h",
     "test/test_doh_server.cc",
@@ -2195,13 +2191,6 @@
     "url_request/url_request_test_util.h",
   ]
 
-  if (is_win) {
-    sources += [
-      "test/win/fake_network_cost_manager.cc",
-      "test/win/fake_network_cost_manager.h",
-    ]
-  }
-
   if (is_mac) {
     sources += [
       "test/keychain_test_util_mac.cc",
@@ -4439,7 +4428,6 @@
   if (is_win) {
     sources += [
       "base/network_change_notifier_win_unittest.cc",
-      "base/network_cost_change_notifier_win_unittest.cc",
       "base/network_interfaces_win_unittest.cc",
       "cert/cert_verify_proc_win_unittest.cc",
       "http/http_auth_sspi_win_unittest.cc",
diff --git a/net/base/connection_endpoint_metadata.cc b/net/base/connection_endpoint_metadata.cc
index 10b1968..e9a19473 100644
--- a/net/base/connection_endpoint_metadata.cc
+++ b/net/base/connection_endpoint_metadata.cc
@@ -21,6 +21,15 @@
 }  // namespace
 
 ConnectionEndpointMetadata::ConnectionEndpointMetadata() = default;
+
+ConnectionEndpointMetadata::ConnectionEndpointMetadata(
+    std::vector<std::string> supported_protocol_alpns,
+    EchConfigList ech_config_list,
+    std::string target_name)
+    : supported_protocol_alpns(std::move(supported_protocol_alpns)),
+      ech_config_list(std::move(ech_config_list)),
+      target_name(std::move(target_name)) {}
+
 ConnectionEndpointMetadata::~ConnectionEndpointMetadata() = default;
 ConnectionEndpointMetadata::ConnectionEndpointMetadata(
     const ConnectionEndpointMetadata&) = default;
diff --git a/net/base/connection_endpoint_metadata.h b/net/base/connection_endpoint_metadata.h
index 8d81b5a..eeeac225 100644
--- a/net/base/connection_endpoint_metadata.h
+++ b/net/base/connection_endpoint_metadata.h
@@ -25,6 +25,9 @@
   using EchConfigList = std::vector<uint8_t>;
 
   ConnectionEndpointMetadata();
+  ConnectionEndpointMetadata(std::vector<std::string> supported_protocol_alpns,
+                             EchConfigList ech_config_list,
+                             std::string target_name);
   ~ConnectionEndpointMetadata();
 
   ConnectionEndpointMetadata(const ConnectionEndpointMetadata&);
diff --git a/net/base/load_flags_list.h b/net/base/load_flags_list.h
index 38062cf..f936d95 100644
--- a/net/base/load_flags_list.h
+++ b/net/base/load_flags_list.h
@@ -21,8 +21,9 @@
 // All other caches are used as normal.
 LOAD_FLAG(VALIDATE_CACHE, 1 << 0)
 
-// This is "shift-reload", meaning a "pragma: no-cache" end-to-end fetch. All
-// other caches are used as normal.
+// This is "shift-reload", meaning a "pragma: no-cache" end-to-end fetch.
+// The response is not read from the HTTP cache but is written to the cache,
+// unlike in `DISABLE_CACHE`. All other caches are used as normal.
 LOAD_FLAG(BYPASS_CACHE, 1 << 1)
 
 // This is a back/forward style navigation where the cached content should
@@ -33,8 +34,9 @@
 // resource from the cache (or some equivalent local store).
 LOAD_FLAG(ONLY_FROM_CACHE, 1 << 3)
 
-// This is a navigation that will not use the cache at all. It does not
-// impact the HTTP request headers. All other caches are used as normal.
+// This is a request whose response will not be read or written to the HTTP
+// cache. It does not impact the cache-related HTTP request headers. All other
+// caches are used as normal.
 LOAD_FLAG(DISABLE_CACHE, 1 << 4)
 
 // If present, causes dependent network fetches (AIA, CRLs, OCSP) to be
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index cc8d2bcb..233bb3d 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -248,6 +248,11 @@
   const scoped_refptr<
       base::ObserverListThreadSafe<DefaultNetworkActiveObserver>>
       default_network_active_observer_list_;
+
+  // Indicates if connection cost observer was added before
+  // network_change_notifier was initialized, if so ConnectionCostObserverAdded
+  // is invoked from constructor.
+  std::atomic_bool connection_cost_observers_added_ = false;
 };
 
 class NetworkChangeNotifier::SystemDnsConfigObserver
@@ -682,8 +687,13 @@
 void NetworkChangeNotifier::AddConnectionCostObserver(
     ConnectionCostObserver* observer) {
   DCHECK(!observer->observer_list_);
+  GetObserverList().connection_cost_observers_added_ = true;
   observer->observer_list_ = GetObserverList().connection_cost_observer_list_;
   observer->observer_list_->AddObserver(observer);
+  base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
+  if (g_network_change_notifier) {
+    g_network_change_notifier->ConnectionCostObserverAdded();
+  }
 }
 
 void NetworkChangeNotifier::AddDefaultNetworkActiveObserver(
@@ -842,6 +852,9 @@
     g_network_change_notifier = this;
 
     system_dns_config_notifier_->AddObserver(system_dns_config_observer_.get());
+    if (GetObserverList().connection_cost_observers_added_) {
+      g_network_change_notifier->ConnectionCostObserverAdded();
+    }
   }
   if (!omit_observers_in_constructor_for_testing) {
     network_change_calculator_ =
diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h
index c79374c..93108a8 100644
--- a/net/base/network_change_notifier.h
+++ b/net/base/network_change_notifier.h
@@ -670,6 +670,13 @@
   // as early as possible in the destructor to prevent races.
   void ClearGlobalPointer();
 
+  // Called whenever a new ConnectionCostObserver is added. This method is
+  // needed so that the implementation class can be notified and
+  // potentially take action when an observer gets added. Since the act of
+  // adding an observer and the observer list itself are both static, the
+  // implementation class has no direct capability to watch for changes.
+  virtual void ConnectionCostObserverAdded() {}
+
   // Listening for notifications of this type is expensive as they happen
   // frequently. For this reason, we report {de}registration to the
   // implementation class, so that it can decide to only listen to this type of
diff --git a/net/base/network_change_notifier_unittest.cc b/net/base/network_change_notifier_unittest.cc
index 89013ac4..3f3fbd6 100644
--- a/net/base/network_change_notifier_unittest.cc
+++ b/net/base/network_change_notifier_unittest.cc
@@ -8,7 +8,6 @@
 #include "build/build_config.h"
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/network_interfaces.h"
-#include "net/test/test_connection_cost_observer.h"
 #include "net/test/test_with_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -227,17 +226,37 @@
   NetworkChangeNotifier::RemoveDNSObserver(&observer);
 }
 
+class TestConnectionCostObserver
+    : public NetworkChangeNotifier::ConnectionCostObserver {
+ public:
+  void OnConnectionCostChanged(
+      NetworkChangeNotifier::ConnectionCost cost) override {
+    cost_changed_inputs_.push_back(cost);
+    ++cost_changed_calls_;
+  }
+
+  int cost_changed_calls() const { return cost_changed_calls_; }
+  std::vector<NetworkChangeNotifier::ConnectionCost> cost_changed_inputs()
+      const {
+    return cost_changed_inputs_;
+  }
+
+ private:
+  int cost_changed_calls_ = 0;
+  std::vector<NetworkChangeNotifier::ConnectionCost> cost_changed_inputs_;
+};
+
 TEST_F(NetworkChangeNotifierMockedTest, TriggerConnectionCostChange) {
   TestConnectionCostObserver observer;
   NetworkChangeNotifier::AddConnectionCostObserver(&observer);
 
-  ASSERT_EQ(0u, observer.cost_changed_calls());
+  ASSERT_EQ(0, observer.cost_changed_calls());
 
   NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeForTests(
       NetworkChangeNotifier::CONNECTION_COST_METERED);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1u, observer.cost_changed_calls());
+  EXPECT_EQ(1, observer.cost_changed_calls());
   EXPECT_EQ(NetworkChangeNotifier::CONNECTION_COST_METERED,
             observer.cost_changed_inputs()[0]);
 
@@ -246,7 +265,7 @@
       NetworkChangeNotifier::CONNECTION_COST_UNMETERED);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1u, observer.cost_changed_calls());
+  EXPECT_EQ(1, observer.cost_changed_calls());
 }
 
 TEST_F(NetworkChangeNotifierMockedTest, ConnectionCostDefaultsToCellular) {
diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc
index e12985b..234a662c 100644
--- a/net/base/network_change_notifier_win.cc
+++ b/net/base/network_change_notifier_win.cc
@@ -21,7 +21,7 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "net/base/network_cost_change_notifier_win.h"
+#include "base/win/windows_version.h"
 #include "net/base/winsock_init.h"
 #include "net/base/winsock_util.h"
 
@@ -32,8 +32,88 @@
 // Time between NotifyAddrChange retries, on failure.
 const int kWatchForAddressChangeRetryIntervalMs = 500;
 
+HRESULT GetConnectionPoints(IUnknown* manager,
+                            REFIID IIDSyncInterface,
+                            IConnectionPoint** connection_point_raw) {
+  *connection_point_raw = nullptr;
+  Microsoft::WRL::ComPtr<IConnectionPointContainer> connection_point_container;
+  HRESULT hr =
+      manager->QueryInterface(IID_PPV_ARGS(&connection_point_container));
+  if (FAILED(hr))
+    return hr;
+
+  // Find the interface
+  Microsoft::WRL::ComPtr<IConnectionPoint> connection_point;
+  hr = connection_point_container->FindConnectionPoint(IIDSyncInterface,
+                                                       &connection_point);
+  if (FAILED(hr))
+    return hr;
+
+  *connection_point_raw = connection_point.Get();
+  (*connection_point_raw)->AddRef();
+
+  return hr;
+}
+
 }  // namespace
 
+// This class is used as an event sink to register for notifications from the
+// INetworkCostManagerEvents interface. In particular, we are focused on getting
+// notified when the Connection Cost changes. This is only supported on Win10+.
+class NetworkCostManagerEventSink
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          INetworkCostManagerEvents> {
+ public:
+  using CostChangedCallback = base::RepeatingCallback<void()>;
+
+  NetworkCostManagerEventSink(INetworkCostManager* cost_manager,
+                              const CostChangedCallback& callback)
+      : network_cost_manager_(cost_manager), cost_changed_callback_(callback) {}
+  ~NetworkCostManagerEventSink() override = default;
+
+  // INetworkCostManagerEvents members
+  IFACEMETHODIMP CostChanged(_In_ DWORD cost,
+                             _In_opt_ NLM_SOCKADDR* /*pSockAddr*/) override {
+    cost_changed_callback_.Run();
+    return S_OK;
+  }
+
+  IFACEMETHODIMP DataPlanStatusChanged(
+      _In_opt_ NLM_SOCKADDR* /*pSockAddr*/) override {
+    return S_OK;
+  }
+
+  HRESULT RegisterForNotifications() {
+    Microsoft::WRL::ComPtr<IUnknown> unknown;
+    HRESULT hr = QueryInterface(IID_PPV_ARGS(&unknown));
+    if (hr != S_OK)
+      return hr;
+
+    hr = GetConnectionPoints(network_cost_manager_.Get(),
+                             IID_INetworkCostManagerEvents, &connection_point_);
+    if (hr != S_OK)
+      return hr;
+
+    hr = connection_point_->Advise(unknown.Get(), &cookie_);
+    return hr;
+  }
+
+  void UnRegisterForNotifications() {
+    if (connection_point_) {
+      connection_point_->Unadvise(cookie_);
+      connection_point_ = nullptr;
+      cookie_ = 0;
+    }
+  }
+
+ private:
+  Microsoft::WRL::ComPtr<INetworkCostManager> network_cost_manager_;
+  Microsoft::WRL::ComPtr<IConnectionPoint> connection_point_;
+  DWORD cookie_ = 0;
+  CostChangedCallback cost_changed_callback_;
+};
+
 NetworkChangeNotifierWin::NetworkChangeNotifierWin()
     : NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()),
       blocking_task_runner_(
@@ -45,10 +125,6 @@
           base::SequencedTaskRunnerHandle::Get()) {
   memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
   addr_overlapped_.hEvent = WSACreateEvent();
-
-  cost_change_notifier_ = NetworkCostChangeNotifierWin::CreateInstance(
-      base::BindRepeating(&NetworkChangeNotifierWin::OnCostChanged,
-                          weak_factory_.GetWeakPtr()));
 }
 
 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
@@ -59,6 +135,11 @@
     addr_watcher_.StopWatching();
   }
   WSACloseEvent(addr_overlapped_.hEvent);
+
+  if (network_cost_manager_event_sink_) {
+    network_cost_manager_event_sink_->UnRegisterForNotifications();
+    network_cost_manager_event_sink_ = nullptr;
+  }
 }
 
 // static
@@ -200,23 +281,115 @@
 
 NetworkChangeNotifier::ConnectionCost
 NetworkChangeNotifierWin::GetCurrentConnectionCost() {
-  if (last_computed_connection_cost_ ==
-      ConnectionCost::CONNECTION_COST_UNKNOWN) {
-    // Use the default logic when the Windows OS APIs do not have a cost for the
-    // current connection.
+  InitializeConnectionCost();
+
+  // Pre-Win10 use the default logic.
+  if (base::win::GetVersion() < base::win::Version::WIN10)
     return NetworkChangeNotifier::GetCurrentConnectionCost();
-  }
+
+  // If we don't have the event sink we aren't registered for automatic updates.
+  // In that case, we need to update the value at the time it is requested.
+  if (!network_cost_manager_event_sink_)
+    UpdateConnectionCostFromCostManager();
+
   return last_computed_connection_cost_;
 }
 
-void NetworkChangeNotifierWin::OnCostChanged(
-    NetworkChangeNotifier::ConnectionCost new_cost) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+bool NetworkChangeNotifierWin::InitializeConnectionCostOnce() {
+  // Pre-Win10 this information cannot be retrieved and cached.
+  if (base::win::GetVersion() < base::win::Version::WIN10) {
+    SetCurrentConnectionCost(CONNECTION_COST_UNKNOWN);
+    return true;
+  }
 
+  HRESULT hr =
+      ::CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_ALL,
+                         IID_INetworkCostManager, &network_cost_manager_);
+  if (FAILED(hr)) {
+    SetCurrentConnectionCost(CONNECTION_COST_UNKNOWN);
+    return true;
+  }
+
+  UpdateConnectionCostFromCostManager();
+
+  return true;
+}
+
+void NetworkChangeNotifierWin::InitializeConnectionCost() {
+  static bool g_connection_cost_initialized = InitializeConnectionCostOnce();
+  DCHECK(g_connection_cost_initialized);
+}
+
+HRESULT NetworkChangeNotifierWin::UpdateConnectionCostFromCostManager() {
+  if (!network_cost_manager_)
+    return E_ABORT;
+
+  DWORD cost = NLM_CONNECTION_COST_UNKNOWN;
+  HRESULT hr = network_cost_manager_->GetCost(&cost, nullptr);
+  if (FAILED(hr)) {
+    SetCurrentConnectionCost(CONNECTION_COST_UNKNOWN);
+  } else {
+    SetCurrentConnectionCost(
+        ConnectionCostFromNlmCost((NLM_CONNECTION_COST)cost));
+  }
+  return hr;
+}
+
+// static
+NetworkChangeNotifier::ConnectionCost
+NetworkChangeNotifierWin::ConnectionCostFromNlmCost(NLM_CONNECTION_COST cost) {
+  if (cost == NLM_CONNECTION_COST_UNKNOWN)
+    return CONNECTION_COST_UNKNOWN;
+  else if ((cost & NLM_CONNECTION_COST_UNRESTRICTED) != 0)
+    return CONNECTION_COST_UNMETERED;
+  else
+    return CONNECTION_COST_METERED;
+}
+
+void NetworkChangeNotifierWin::SetCurrentConnectionCost(
+    ConnectionCost connection_cost) {
+  last_computed_connection_cost_ = connection_cost;
+}
+
+void NetworkChangeNotifierWin::OnCostChanged() {
+  ConnectionCost old_cost = last_computed_connection_cost_;
+  // It is possible to get multiple notifications in a short period of time.
+  // Rather than worrying about whether this notification represents the latest,
+  // just get the current value from the CostManager so we know that we're
+  // actually getting the correct value.
+  UpdateConnectionCostFromCostManager();
   // Only notify if there's actually a change.
-  if (last_computed_connection_cost_ != new_cost) {
-    last_computed_connection_cost_ = new_cost;
+  if (old_cost != GetCurrentConnectionCost())
     NotifyObserversOfConnectionCostChange();
+}
+
+void NetworkChangeNotifierWin::ConnectionCostObserverAdded() {
+  sequence_runner_for_registration_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&NetworkChangeNotifierWin::OnConnectionCostObserverAdded,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void NetworkChangeNotifierWin::OnConnectionCostObserverAdded() {
+  DCHECK(sequence_runner_for_registration_->RunsTasksInCurrentSequence());
+  InitializeConnectionCost();
+
+  // No need to register if we don't have a cost manager or if we're already
+  // registered.
+  if (!network_cost_manager_ || network_cost_manager_event_sink_)
+    return;
+
+  network_cost_manager_event_sink_ =
+      Microsoft::WRL::Make<net::NetworkCostManagerEventSink>(
+          network_cost_manager_.Get(),
+          base::BindRepeating(&NetworkChangeNotifierWin::OnCostChanged,
+                              weak_factory_.GetWeakPtr()));
+  HRESULT hr = network_cost_manager_event_sink_->RegisterForNotifications();
+  if (hr != S_OK) {
+    // If registration failed for any reason, just destroy the event sink. The
+    // observer will remain connected but will not receive any updates. If
+    // another observer gets added later, we can re-attempt registration.
+    network_cost_manager_event_sink_ = nullptr;
   }
 }
 
@@ -318,8 +491,8 @@
   offline_polls_++;
   // If we continue to appear offline, delay sending out the notification in
   // case we appear to go online within 20 seconds.  UMA histogram data shows
-  // we may not detect the transition to online state after 1 second but
-  // within 20 seconds we generally do.
+  // we may not detect the transition to online state after 1 second but within
+  // 20 seconds we generally do.
   if (last_announced_offline_ && current_offline && offline_polls_ <= 20) {
     timer_.Start(FROM_HERE, base::Seconds(1), this,
                  &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange);
diff --git a/net/base/network_change_notifier_win.h b/net/base/network_change_notifier_win.h
index c89af03..0d88afa 100644
--- a/net/base/network_change_notifier_win.h
+++ b/net/base/network_change_notifier_win.h
@@ -5,9 +5,13 @@
 #ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_
 #define NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_
 
+#include <netlistmgr.h>
+#include <ocidl.h>
 #include <windows.h>
+#include <wrl.h>
+#include <wrl/client.h>
 
-#include <atomic>
+#include <memory>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
@@ -15,7 +19,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
-#include "base/threading/sequence_bound.h"
 #include "base/timer/timer.h"
 #include "base/win/object_watcher.h"
 #include "net/base/net_export.h"
@@ -27,7 +30,7 @@
 
 namespace net {
 
-class NetworkCostChangeNotifierWin;
+class NetworkCostManagerEventSink;
 
 // NetworkChangeNotifierWin uses a SequenceChecker, as all its internal
 // notification code must be called on the sequence it is created and destroyed
@@ -97,7 +100,25 @@
 
   static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsWin();
 
-  void OnCostChanged(NetworkChangeNotifier::ConnectionCost new_cost);
+  // Gets the current network connection cost (if possible) and caches it.
+  void InitializeConnectionCost();
+  // Does the work of initializing for thread safety.
+  bool InitializeConnectionCostOnce();
+  // Retrieves the current network connection cost from the OS's Cost Manager.
+  HRESULT UpdateConnectionCostFromCostManager();
+  // Converts the OS enum values to the enum values used in our code.
+  static ConnectionCost ConnectionCostFromNlmCost(NLM_CONNECTION_COST cost);
+  // Sets the cached network connection cost value.
+  void SetCurrentConnectionCost(ConnectionCost connection_cost);
+  // Callback method for the notification event sink.
+  void OnCostChanged();
+  // Tells this class that an observer was added and therefore this class needs
+  // to register for notifications.
+  void ConnectionCostObserverAdded() override;
+  // Since ConnectionCostObserverAdded() can be called on any thread and we
+  // don't want to do a bunch of work on an arbitrary thread, this method used
+  // to post task to do the work.
+  void OnConnectionCostObserverAdded();
 
   // All member variables may only be accessed on the sequence |this| was
   // created on.
@@ -120,13 +141,8 @@
   mutable base::Lock last_computed_connection_type_lock_;
   ConnectionType last_computed_connection_type_;
 
-  std::atomic<NetworkChangeNotifier::ConnectionCost>
-      last_computed_connection_cost_ =
-          NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN;
-
-  // Provides the cost of the current connection.  Uses the Windows OS APIs to
-  // monitor and determine cost.
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier_;
+  std::atomic<ConnectionCost> last_computed_connection_cost_ =
+      ConnectionCost::CONNECTION_COST_UNKNOWN;
 
   // Result of IsOffline() when NotifyObserversOfConnectionTypeChange()
   // was last called.
@@ -134,6 +150,10 @@
   // Number of times polled to check if still offline.
   int offline_polls_;
 
+  Microsoft::WRL::ComPtr<INetworkCostManager> network_cost_manager_;
+  Microsoft::WRL::ComPtr<NetworkCostManagerEventSink>
+      network_cost_manager_event_sink_;
+
   // Used to ensure that all registration actions are properly sequenced on the
   // same thread regardless of which thread was used to call into the
   // NetworkChangeNotifier API.
diff --git a/net/base/network_change_notifier_win_unittest.cc b/net/base/network_change_notifier_win_unittest.cc
index cbb1b24..9abe200 100644
--- a/net/base/network_change_notifier_win_unittest.cc
+++ b/net/base/network_change_notifier_win_unittest.cc
@@ -4,22 +4,16 @@
 
 #include "net/base/network_change_notifier_win.h"
 
-#include <memory>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/run_loop.h"
-#include "base/sequence_checker.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/test/scoped_os_info_override_win.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/win/windows_version.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/network_change_notifier_factory.h"
-#include "net/test/test_connection_cost_observer.h"
 #include "net/test/test_with_task_environment.h"
-#include "net/test/win/fake_network_cost_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,6 +31,7 @@
   TestNetworkChangeNotifierWin() {
     last_computed_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
     last_announced_offline_ = false;
+    last_computed_connection_cost_ = ConnectionCost::CONNECTION_COST_UNKNOWN;
     sequence_runner_for_registration_ = base::SequencedTaskRunnerHandle::Get();
   }
 
@@ -60,18 +55,13 @@
 
   // From NetworkChangeNotifierWin.
   MOCK_METHOD0(WatchForAddressChangeInternal, bool());
-
-  // Allow tests to compare results with the default implementation that does
-  // not depend on the INetworkCostManager Windows OS API.  The default
-  // implementation is used as a fall back when INetworkCostManager fails.
-  ConnectionCost GetCurrentConnectionCostFromDefaultImplementation() {
-    return NetworkChangeNotifier::GetCurrentConnectionCost();
-  }
 };
 
 class TestIPAddressObserver : public NetworkChangeNotifier::IPAddressObserver {
  public:
-  TestIPAddressObserver() { NetworkChangeNotifier::AddIPAddressObserver(this); }
+  TestIPAddressObserver() {
+    NetworkChangeNotifier::AddIPAddressObserver(this);
+  }
 
   TestIPAddressObserver(const TestIPAddressObserver&) = delete;
   TestIPAddressObserver& operator=(const TestIPAddressObserver&) = delete;
@@ -192,8 +182,7 @@
         .Times(1)
         .WillOnce(Invoke(&run_loop, &base::RunLoop::QuitWhenIdle));
     EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
-        .Times(1)
-        .WillOnce(Return(true));
+        .Times(1).WillOnce(Return(true));
 
     run_loop.Run();
 
@@ -230,19 +219,23 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  bool HasNetworkCostManager() {
+    return network_change_notifier_.network_cost_manager_.Get() != nullptr;
+  }
+
+  bool HasNetworkCostManagerEventSink() {
+    return network_change_notifier_.network_cost_manager_event_sink_.Get() !=
+           nullptr;
+  }
+
+  NetworkChangeNotifier::ConnectionCost LastComputedConnectionCost() {
+    return network_change_notifier_.last_computed_connection_cost_;
+  }
+
   NetworkChangeNotifier::ConnectionCost GetCurrentConnectionCost() {
     return network_change_notifier_.GetCurrentConnectionCost();
   }
 
-  NetworkChangeNotifier::ConnectionCost
-  GetCurrentConnectionCostFromDefaultImplementation() {
-    return network_change_notifier_
-        .GetCurrentConnectionCostFromDefaultImplementation();
-  }
-
- protected:
-  FakeNetworkCostManagerEnvironment fake_network_cost_manager_environment_;
-
  private:
   // Note that the order of declaration here is important.
 
@@ -294,102 +287,58 @@
   RetryAndSucceed();
 }
 
-TEST_F(NetworkChangeNotifierWinTest, GetCurrentCost) {
-  if (base::win::GetVersion() < base::win::Version::WIN10)
-    return;
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  // Wait for NetworkCostChangeNotifierWin to finish initializing.
-  RunUntilIdle();
-
-  EXPECT_EQ(GetCurrentConnectionCost(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-
-  // Wait for NetworkCostChangeNotifierWin to handle the cost changed event.
-  RunUntilIdle();
-
-  EXPECT_EQ(GetCurrentConnectionCost(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-}
-
-TEST_F(NetworkChangeNotifierWinTest, CostChangeObserver) {
-  if (base::win::GetVersion() < base::win::Version::WIN10)
-    return;
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  // Wait for NetworkCostChangeNotifierWin to finish initializing.
-  RunUntilIdle();
-
-  TestConnectionCostObserver cost_observer;
-  NetworkChangeNotifier::AddConnectionCostObserver(&cost_observer);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-
-  cost_observer.WaitForConnectionCostChanged();
-
-  ASSERT_EQ(cost_observer.cost_changed_calls(), 1u);
-  EXPECT_EQ(cost_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-
-  NetworkChangeNotifier::RemoveConnectionCostObserver(&cost_observer);
-}
-
-// Uses the fake implementation of INetworkCostManager to simulate GetCost()
-// returning an error HRESULT.
-class NetworkChangeNotifierWinCostErrorTest
-    : public NetworkChangeNotifierWinTest {
-  void SetUp() override {
-    if (base::win::GetVersion() < base::win::Version::WIN10) {
-      GTEST_SKIP();
-    }
-
-    fake_network_cost_manager_environment_.SimulateError(
-        NetworkCostManagerStatus::kErrorGetCostFailed);
-
-    NetworkChangeNotifierWinTest::SetUp();
-  }
-};
-
-TEST_F(NetworkChangeNotifierWinCostErrorTest, CostError) {
-  // Wait for NetworkCostChangeNotifierWin to finish initializing, which should
-  // fail with an error.
-  RunUntilIdle();
-
-  // NetworkChangeNotifierWin must use the default implementation when
-  // NetworkCostChangeNotifierWin returns an unknown cost.
-  EXPECT_EQ(GetCurrentConnectionCost(),
-            GetCurrentConnectionCostFromDefaultImplementation());
-}
-
-// Override the Windows OS version to simulate running on an OS that does not
-// support INetworkCostManager.
-class NetworkChangeNotifierWinCostUnsupportedOsTest
-    : public NetworkChangeNotifierWinTest {
+class TestConnectionCostObserver
+    : public NetworkChangeNotifier::ConnectionCostObserver {
  public:
-  NetworkChangeNotifierWinCostUnsupportedOsTest()
-      : os_override_(base::test::ScopedOSInfoOverride::Type::kWin81Pro) {}
+  TestConnectionCostObserver() {}
 
- protected:
-  base::test::ScopedOSInfoOverride os_override_;
+  TestConnectionCostObserver(const TestConnectionCostObserver&) = delete;
+  TestConnectionCostObserver& operator=(const TestConnectionCostObserver&) =
+      delete;
+
+  ~TestConnectionCostObserver() override {
+    NetworkChangeNotifier::RemoveConnectionCostObserver(this);
+  }
+
+  void OnConnectionCostChanged(NetworkChangeNotifier::ConnectionCost) override {
+  }
+
+  void Register() { NetworkChangeNotifier::AddConnectionCostObserver(this); }
 };
 
-TEST_F(NetworkChangeNotifierWinCostUnsupportedOsTest, CostWithUnsupportedOS) {
-  // Wait for NetworkCostChangeNotifierWin to finish initializing, which should
-  // initialize with an unknown cost on an unsupported OS.
-  RunUntilIdle();
+TEST_F(NetworkChangeNotifierWinTest, NetworkCostManagerIntegration) {
+  // NetworkCostManager integration only exist on Win10+.
+  if (base::win::GetVersion() < base::win::Version::WIN10)
+    return;
 
-  // NetworkChangeNotifierWin must use the default implementation when
-  // NetworkCostChangeNotifierWin returns an unknown cost.
-  EXPECT_EQ(GetCurrentConnectionCost(),
-            GetCurrentConnectionCostFromDefaultImplementation());
+  // Upon creation, none of the NetworkCostManager integration should be
+  // initialized yet.
+  ASSERT_FALSE(HasNetworkCostManager());
+  ASSERT_FALSE(HasNetworkCostManagerEventSink());
+  ASSERT_EQ(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN,
+            LastComputedConnectionCost());
+
+  // Asking for the current connection cost should initialize the
+  // NetworkCostManager integration, but not the event sink.
+  // Note that the actual ConnectionCost value return is irrelevant beyond the
+  // fact that it shouldn't be UNKNOWN anymore if the integration is initialized
+  // properly.
+  NetworkChangeNotifier::ConnectionCost current_connection_cost =
+      GetCurrentConnectionCost();
+  EXPECT_NE(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN,
+            current_connection_cost);
+  EXPECT_EQ(current_connection_cost, LastComputedConnectionCost());
+  EXPECT_TRUE(HasNetworkCostManager());
+  EXPECT_FALSE(HasNetworkCostManagerEventSink());
+
+  // Adding a ConnectionCostObserver should initialize the event sink. If the
+  // subsequent registration for updates fails, the event sink will get
+  // destroyed.
+  TestConnectionCostObserver test_connection_cost_observer;
+  test_connection_cost_observer.Register();
+  // The actual registration happens on a callback, so need to run until idle.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(HasNetworkCostManagerEventSink());
 }
 
 }  // namespace net
diff --git a/net/base/network_cost_change_notifier_win.cc b/net/base/network_cost_change_notifier_win.cc
deleted file mode 100644
index abc56a41..0000000
--- a/net/base/network_cost_change_notifier_win.cc
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/base/network_cost_change_notifier_win.h"
-
-#include <wrl.h>
-#include <wrl/client.h>
-
-#include "base/check.h"
-#include "base/no_destructor.h"
-#include "base/task/bind_post_task.h"
-#include "base/task/thread_pool.h"
-#include "base/threading/scoped_thread_priority.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/win/com_init_util.h"
-#include "base/win/windows_version.h"
-
-using Microsoft::WRL::ComPtr;
-
-namespace net {
-
-namespace {
-
-NetworkChangeNotifier::ConnectionCost ConnectionCostFromNlmConnectionCost(
-    DWORD connection_cost_flags) {
-  if (connection_cost_flags == NLM_CONNECTION_COST_UNKNOWN)
-    return NetworkChangeNotifier::CONNECTION_COST_UNKNOWN;
-  else if ((connection_cost_flags & NLM_CONNECTION_COST_UNRESTRICTED) != 0)
-    return NetworkChangeNotifier::CONNECTION_COST_UNMETERED;
-  else
-    return NetworkChangeNotifier::CONNECTION_COST_METERED;
-}
-
-NetworkCostChangeNotifierWin::CoCreateInstanceCallback&
-GetCoCreateInstanceOverride() {
-  static base::NoDestructor<
-      NetworkCostChangeNotifierWin::CoCreateInstanceCallback>
-      callback_for_testing;
-  return *callback_for_testing;
-}
-
-}  // namespace
-
-// This class is used as an event sink to register for notifications from the
-// INetworkCostManagerEvents interface. In particular, we are focused on getting
-// notified when the connection cost changes. This is only supported on Win10+.
-class NetworkCostManagerEventSinkWin final
-    : public Microsoft::WRL::RuntimeClass<
-          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
-          INetworkCostManagerEvents> {
- public:
-  static HRESULT CreateInstance(
-      INetworkCostManager* network_cost_manager,
-      base::RepeatingClosure cost_changed_callback,
-      ComPtr<NetworkCostManagerEventSinkWin>* result) {
-    ComPtr<NetworkCostManagerEventSinkWin> instance =
-        Microsoft::WRL::Make<net::NetworkCostManagerEventSinkWin>(
-            cost_changed_callback);
-    HRESULT hr = instance->RegisterForNotifications(network_cost_manager);
-    if (hr != S_OK) {
-      return hr;
-    }
-
-    *result = instance;
-    return S_OK;
-  }
-
-  void UnRegisterForNotifications() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    if (event_sink_connection_point_) {
-      event_sink_connection_point_->Unadvise(event_sink_connection_cookie_);
-      event_sink_connection_point_.Reset();
-    }
-  }
-
-  // Implement the INetworkCostManagerEvents interface.
-  HRESULT __stdcall CostChanged(DWORD /*cost*/,
-                                NLM_SOCKADDR* /*socket_address*/) final {
-    // It is possible to get multiple notifications in a short period of time.
-    // Rather than worrying about whether this notification represents the
-    // latest, just notify the owner who can get the current value from the
-    // INetworkCostManager so we know that we're actually getting the correct
-    // value.
-    cost_changed_callback_.Run();
-    return S_OK;
-  }
-
-  HRESULT __stdcall DataPlanStatusChanged(
-      NLM_SOCKADDR* /*socket_address*/) final {
-    return S_OK;
-  }
-
-  NetworkCostManagerEventSinkWin(base::RepeatingClosure cost_changed_callback)
-      : cost_changed_callback_(cost_changed_callback) {}
-
-  NetworkCostManagerEventSinkWin(const NetworkCostManagerEventSinkWin&) =
-      delete;
-  NetworkCostManagerEventSinkWin& operator=(
-      const NetworkCostManagerEventSinkWin&) = delete;
-
- private:
-  ~NetworkCostManagerEventSinkWin() final = default;
-
-  HRESULT RegisterForNotifications(INetworkCostManager* cost_manager) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK_GE(base::win::GetVersion(), base::win::Version::WIN10);
-
-    base::win::AssertComInitialized();
-    base::win::AssertComApartmentType(base::win::ComApartmentType::STA);
-
-    ComPtr<IUnknown> this_event_sink_unknown;
-    HRESULT hr = QueryInterface(IID_PPV_ARGS(&this_event_sink_unknown));
-
-    // `NetworkCostManagerEventSinkWin::QueryInterface` for `IUnknown` must
-    // succeed since it is implemented by this class.
-    DCHECK_EQ(hr, S_OK);
-
-    ComPtr<IConnectionPointContainer> connection_point_container;
-    hr =
-        cost_manager->QueryInterface(IID_PPV_ARGS(&connection_point_container));
-    if (hr != S_OK) {
-      return hr;
-    }
-
-    Microsoft::WRL::ComPtr<IConnectionPoint> event_sink_connection_point;
-    hr = connection_point_container->FindConnectionPoint(
-        IID_INetworkCostManagerEvents, &event_sink_connection_point);
-    if (hr != S_OK) {
-      return hr;
-    }
-
-    hr = event_sink_connection_point->Advise(this_event_sink_unknown.Get(),
-                                             &event_sink_connection_cookie_);
-    if (hr != S_OK) {
-      return hr;
-    }
-
-    DCHECK_EQ(event_sink_connection_point_, nullptr);
-    event_sink_connection_point_ = event_sink_connection_point;
-    return S_OK;
-  }
-
-  base::RepeatingClosure cost_changed_callback_;
-
-  // The following members must be accessed on the sequence from
-  // `sequence_checker_`
-  SEQUENCE_CHECKER(sequence_checker_);
-  DWORD event_sink_connection_cookie_ = 0;
-  Microsoft::WRL::ComPtr<IConnectionPoint> event_sink_connection_point_;
-};
-
-// static
-base::SequenceBound<NetworkCostChangeNotifierWin>
-NetworkCostChangeNotifierWin::CreateInstance(
-    CostChangedCallback cost_changed_callback) {
-  scoped_refptr<base::SequencedTaskRunner> com_best_effort_task_runner =
-      base::ThreadPool::CreateCOMSTATaskRunner(
-          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
-
-  return base::SequenceBound<NetworkCostChangeNotifierWin>(
-      com_best_effort_task_runner,
-      // Ensure `cost_changed_callback` runs on the sequence of the creator and
-      // owner of `NetworkCostChangeNotifierWin`.
-      base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
-                         cost_changed_callback));
-}
-
-NetworkCostChangeNotifierWin::NetworkCostChangeNotifierWin(
-    CostChangedCallback cost_changed_callback)
-    : cost_changed_callback_(cost_changed_callback) {
-  StartWatching();
-}
-
-NetworkCostChangeNotifierWin::~NetworkCostChangeNotifierWin() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  StopWatching();
-}
-
-void NetworkCostChangeNotifierWin::StartWatching() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (base::win::GetComApartmentTypeForThread() ==
-      base::win::ComApartmentType::NONE) {
-    // TODO(crbug.com/1367360): INetworkCostManager inaccessible in network
-    // sandbox.  Currently, the network sandbox does not allow COM.
-    return;
-  }
-
-  base::win::AssertComInitialized();
-  base::win::AssertComApartmentType(base::win::ComApartmentType::STA);
-
-  if (base::win::GetVersion() < base::win::Version::WIN10) {
-    // INetworkCostManager requires Win10 or higher.
-    return;
-  }
-
-  SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
-
-  // Create INetworkListManager using CoCreateInstance, but allow tests to
-  // provide an fake implementation of INetworkListManager through an override.
-  CoCreateInstanceCallback co_create_instance_callback =
-      base::BindRepeating(&CoCreateInstance);
-  if (GetCoCreateInstanceOverride()) {
-    co_create_instance_callback = GetCoCreateInstanceOverride();
-  }
-
-  ComPtr<INetworkCostManager> cost_manager;
-  HRESULT hr = co_create_instance_callback.Run(
-      CLSID_NetworkListManager, /*unknown_outer=*/nullptr, CLSCTX_ALL,
-      IID_INetworkCostManager, &cost_manager);
-  if (hr != S_OK) {
-    return;
-  }
-
-  // Subscribe to cost changed events.
-  hr = NetworkCostManagerEventSinkWin::CreateInstance(
-      cost_manager.Get(),
-      // Cost changed callbacks must run on this sequence to get the new cost
-      // from `INetworkCostManager`.
-      base::BindPostTask(
-          base::SequencedTaskRunnerHandle::Get(),
-          base::BindRepeating(&NetworkCostChangeNotifierWin::HandleCostChanged,
-                              weak_ptr_factory_.GetWeakPtr())),
-      &cost_manager_event_sink_);
-
-  if (hr != S_OK) {
-    return;
-  }
-
-  // Set the initial cost and inform observers of the initial value.
-  cost_manager_ = cost_manager;
-  HandleCostChanged();
-}
-
-void NetworkCostChangeNotifierWin::StopWatching() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (cost_manager_event_sink_) {
-    cost_manager_event_sink_->UnRegisterForNotifications();
-    cost_manager_event_sink_.Reset();
-  }
-
-  cost_manager_.Reset();
-}
-
-void NetworkCostChangeNotifierWin::HandleCostChanged() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  DWORD connection_cost_flags;
-  HRESULT hr = cost_manager_->GetCost(&connection_cost_flags,
-                                      /*destination_ip_address=*/nullptr);
-  if (hr != S_OK) {
-    connection_cost_flags = NLM_CONNECTION_COST_UNKNOWN;
-  }
-
-  NetworkChangeNotifier::ConnectionCost changed_cost =
-      ConnectionCostFromNlmConnectionCost(connection_cost_flags);
-
-  cost_changed_callback_.Run(changed_cost);
-}
-
-// static
-void NetworkCostChangeNotifierWin::OverrideCoCreateInstanceForTesting(
-    CoCreateInstanceCallback callback_for_testing) {
-  GetCoCreateInstanceOverride() = callback_for_testing;
-}
-
-}  // namespace net
diff --git a/net/base/network_cost_change_notifier_win.h b/net/base/network_cost_change_notifier_win.h
deleted file mode 100644
index 7868cd2..0000000
--- a/net/base/network_cost_change_notifier_win.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_BASE_NETWORK_COST_CHANGE_NOTIFIER_WIN_H_
-#define NET_BASE_NETWORK_COST_CHANGE_NOTIFIER_WIN_H_
-
-#include <netlistmgr.h>
-#include <wrl/client.h>
-
-#include "base/callback_forward.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
-#include "base/threading/sequence_bound.h"
-#include "net/base/net_export.h"
-#include "net/base/network_change_notifier.h"
-
-namespace net {
-class NetworkCostManagerEventSinkWin;
-
-// Uses the `INetworkCostManager` Windows OS API to monitor the cost of the
-// current connection.  `INetworkCostManager` performs blocking IO and
-// synchronous RPC and must be accessed through a thread pool COM STA single
-// threaded task runner.  NetworkCostChangeNotifierWin uses
-// `base::SequenceBound` to prevent these expensive operations from happening on
-// the UI thread.
-class NET_EXPORT_PRIVATE NetworkCostChangeNotifierWin final {
- public:
-  using CostChangedCallback =
-      base::RepeatingCallback<void(NetworkChangeNotifier::ConnectionCost)>;
-
-  // Constructs a new instance using a new COM STA single threaded task runner
-  // to post the task that creates NetworkCostChangeNotifierWin and subscribes
-  // to cost change events.
-  static base::SequenceBound<NetworkCostChangeNotifierWin> CreateInstance(
-      CostChangedCallback cost_changed_callback);
-
-  // Tests use this hook to provide a fake implementation of the OS APIs.
-  // The fake implementation enables tests to simulate different cost values,
-  // cost changed events and OS errors.
-  using CoCreateInstanceCallback = base::RepeatingCallback<
-      HRESULT(REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID*)>;
-  static void OverrideCoCreateInstanceForTesting(
-      CoCreateInstanceCallback callback_for_testing);
-
-  NetworkCostChangeNotifierWin(const NetworkCostChangeNotifierWin&) = delete;
-  NetworkCostChangeNotifierWin& operator=(const NetworkCostChangeNotifierWin&) =
-      delete;
-
- private:
-  friend class base::SequenceBound<NetworkCostChangeNotifierWin>;
-
-  explicit NetworkCostChangeNotifierWin(
-      CostChangedCallback cost_changed_callback);
-  ~NetworkCostChangeNotifierWin();
-
-  // Creates `INetworkCostManager` and subscribe to cost change events.
-  void StartWatching();
-
-  // Stops monitoring the cost of the current connection by unsubscribing to
-  // `INetworkCostManager` events and releasing all members.
-  void StopWatching();
-
-  // Gets the current cost from `cost_manager_` and then runs
-  // `cost_changed_callback_`.
-  void HandleCostChanged();
-
-  // All members must be accessed on the sequence from `sequence_checker_`
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  CostChangedCallback cost_changed_callback_;
-
-  Microsoft::WRL::ComPtr<INetworkCostManager> cost_manager_;
-
-  Microsoft::WRL::ComPtr<NetworkCostManagerEventSinkWin>
-      cost_manager_event_sink_;
-
-  base::WeakPtrFactory<NetworkCostChangeNotifierWin> weak_ptr_factory_{this};
-};
-
-}  // namespace net
-
-#endif  // NET_BASE_NETWORK_COST_CHANGE_NOTIFIER_WIN_H_
diff --git a/net/base/network_cost_change_notifier_win_unittest.cc b/net/base/network_cost_change_notifier_win_unittest.cc
deleted file mode 100644
index ae35acc..0000000
--- a/net/base/network_cost_change_notifier_win_unittest.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/base/network_cost_change_notifier_win.h"
-
-#include "base/run_loop.h"
-#include "base/sequence_checker.h"
-#include "base/test/scoped_os_info_override_win.h"
-#include "base/win/windows_version.h"
-#include "net/base/network_change_notifier.h"
-#include "net/test/test_connection_cost_observer.h"
-#include "net/test/test_with_task_environment.h"
-#include "net/test/win/fake_network_cost_manager.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-class NetworkCostChangeNotifierWinTest : public TestWithTaskEnvironment {
- public:
-  void SetUp() override {
-    if (base::win::GetVersion() < base::win::Version::WIN10) {
-      GTEST_SKIP();
-    }
-  }
-
- protected:
-  FakeNetworkCostManagerEnvironment fake_network_cost_manager_environment_;
-};
-
-TEST_F(NetworkCostChangeNotifierWinTest, InitialCostUnknown) {
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN);
-
-  TestConnectionCostObserver cost_change_observer;
-  auto cost_change_callback =
-      base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged,
-                          base::Unretained(&cost_change_observer));
-
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-      NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-  // Wait for `NetworkCostChangeNotifierWin` to finish initializing.
-  cost_change_observer.WaitForConnectionCostChanged();
-
-  // `NetworkCostChangeNotifierWin` must report an unknown cost after
-  // initializing.
-  EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-  EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN);
-}
-
-TEST_F(NetworkCostChangeNotifierWinTest, InitialCostKnown) {
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  TestConnectionCostObserver cost_change_observer;
-  auto cost_change_callback =
-      base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged,
-                          base::Unretained(&cost_change_observer));
-
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-      NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-  // Initializing changes the cost from unknown to unmetered.
-  cost_change_observer.WaitForConnectionCostChanged();
-
-  ASSERT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-  EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-}
-
-TEST_F(NetworkCostChangeNotifierWinTest, MultipleCostChangedEvents) {
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  TestConnectionCostObserver cost_change_observer;
-  auto cost_change_callback =
-      base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged,
-                          base::Unretained(&cost_change_observer));
-
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-      NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-  // Initializing changes the cost from unknown to unmetered.
-  cost_change_observer.WaitForConnectionCostChanged();
-
-  ASSERT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-  EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-
-  // The simulated event changes the cost from unmetered to metered.
-  cost_change_observer.WaitForConnectionCostChanged();
-
-  ASSERT_EQ(cost_change_observer.cost_changed_calls(), 2u);
-  EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN);
-
-  // The simulated event changes the cost from metered to unknown.
-  cost_change_observer.WaitForConnectionCostChanged();
-
-  ASSERT_EQ(cost_change_observer.cost_changed_calls(), 3u);
-  EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN);
-}
-
-TEST_F(NetworkCostChangeNotifierWinTest, DuplicateEvents) {
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  TestConnectionCostObserver cost_change_observer;
-  auto cost_change_callback =
-      base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged,
-                          base::Unretained(&cost_change_observer));
-
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-      NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-  // Initializing changes the cost from unknown to unmetered.
-  cost_change_observer.WaitForConnectionCostChanged();
-  ASSERT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  cost_change_observer.WaitForConnectionCostChanged();
-
-  // Changing from unmetered to unmetered must dispatch a cost changed event.
-  ASSERT_EQ(cost_change_observer.cost_changed_calls(), 2u);
-  EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-            NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-}
-
-TEST_F(NetworkCostChangeNotifierWinTest, ShutdownImmediately) {
-  TestConnectionCostObserver cost_change_observer;
-  auto cost_change_callback =
-      base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged,
-                          base::Unretained(&cost_change_observer));
-
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-      NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-  // Shutting down immediately must not crash.
-  cost_change_notifier.Reset();
-
-  // Wait for `NetworkCostChangeNotifierWin` to finish initializing and shutting
-  // down.
-  RunUntilIdle();
-
-  // `NetworkCostChangeNotifierWin` reports a connection change after
-  // initializing.
-  EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
-
-  // Wait for `NetworkCostChangeNotifierWin` to handle the cost changed event.
-  RunUntilIdle();
-
-  // After shutdown, cost changed events must have no effect.
-  EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-}
-
-TEST_F(NetworkCostChangeNotifierWinTest, ErrorHandling) {
-  // Simulate the failure of each OS API while initializing
-  // `NetworkCostChangeNotifierWin`.
-  constexpr const NetworkCostManagerStatus kErrorList[] = {
-      NetworkCostManagerStatus::kErrorCoCreateInstanceFailed,
-      NetworkCostManagerStatus::kErrorQueryInterfaceFailed,
-      NetworkCostManagerStatus::kErrorFindConnectionPointFailed,
-      NetworkCostManagerStatus::kErrorAdviseFailed,
-      NetworkCostManagerStatus::kErrorGetCostFailed,
-  };
-  for (auto error : kErrorList) {
-    fake_network_cost_manager_environment_.SetCost(
-        NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-    fake_network_cost_manager_environment_.SimulateError(error);
-
-    TestConnectionCostObserver cost_change_observer;
-    auto cost_change_callback = base::BindRepeating(
-        &TestConnectionCostObserver::OnConnectionCostChanged,
-        base::Unretained(&cost_change_observer));
-
-    base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-        NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-    if (error == NetworkCostManagerStatus::kErrorGetCostFailed) {
-      // `NetworkCostChangeNotifierWin` must report an unknown cost after
-      // `INetworkCostManager::GetCost()` fails.
-      cost_change_observer.WaitForConnectionCostChanged();
-
-      EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u);
-      EXPECT_EQ(cost_change_observer.last_cost_changed_input(),
-                NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN);
-    } else {
-      // Wait for `NetworkCostChangeNotifierWin` to finish initializing.
-      RunUntilIdle();
-
-      // `NetworkCostChangeNotifierWin` must NOT report a changed cost after
-      // failing to initialize.
-      EXPECT_EQ(cost_change_observer.cost_changed_calls(), 0u);
-    }
-  }
-}
-
-TEST_F(NetworkCostChangeNotifierWinTest, UnsupportedOS) {
-  base::test::ScopedOSInfoOverride os_override(
-      base::test::ScopedOSInfoOverride::Type::kWin81Pro);
-
-  fake_network_cost_manager_environment_.SetCost(
-      NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
-
-  TestConnectionCostObserver cost_change_observer;
-  auto cost_change_callback =
-      base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged,
-                          base::Unretained(&cost_change_observer));
-
-  base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier =
-      NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback);
-
-  // Wait for `NetworkCostChangeNotifierWin` to finish initializing.
-  RunUntilIdle();
-
-  // `NetworkCostChangeNotifierWin` must NOT report a changed cost for
-  // unsupported OSes.
-  EXPECT_EQ(cost_change_observer.cost_changed_calls(), 0u);
-}
-
-}  // namespace net
diff --git a/net/dns/BUILD.gn b/net/dns/BUILD.gn
index 4afa392..59f456a 100644
--- a/net/dns/BUILD.gn
+++ b/net/dns/BUILD.gn
@@ -61,6 +61,8 @@
     "dns_util.h",
     "host_cache.cc",
     "host_resolver.cc",
+    "host_resolver_internal_result.cc",
+    "host_resolver_internal_result.h",
     "host_resolver_manager.cc",
     "host_resolver_mdns_listener_impl.cc",
     "host_resolver_mdns_listener_impl.h",
@@ -403,6 +405,7 @@
     "dns_udp_tracker_unittest.cc",
     "dns_util_unittest.cc",
     "host_cache_unittest.cc",
+    "host_resolver_internal_result_unittest.cc",
     "host_resolver_manager_unittest.cc",
     "https_record_rdata_unittest.cc",
     "httpssvc_metrics_unittest.cc",
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index add0637..c1448788 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <iterator>
 #include <map>
+#include <memory>
 #include <ostream>
 #include <string>
 #include <type_traits>
@@ -29,6 +30,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/trace_constants.h"
 #include "net/dns/host_resolver.h"
+#include "net/dns/host_resolver_internal_result.h"
 #include "net/dns/https_record_rdata.h"
 #include "net/dns/public/dns_protocol.h"
 #include "net/dns/public/host_resolver_source.h"
@@ -261,7 +263,7 @@
 HostCache::Entry::Entry(int error,
                         Source source,
                         absl::optional<base::TimeDelta> ttl)
-    : error_(error), source_(source), ttl_(ttl.value_or(base::Seconds(-1))) {
+    : error_(error), source_(source), ttl_(ttl.value_or(kUnknownTtl)) {
   // If |ttl| has a value, must not be negative.
   DCHECK_GE(ttl.value_or(base::TimeDelta()), base::TimeDelta());
   DCHECK_NE(OK, error_);
@@ -275,6 +277,125 @@
                 "`HostCache::Entry::HttpsRecordPriority` must be same type");
 }
 
+HostCache::Entry::Entry(
+    std::vector<std::unique_ptr<HostResolverInternalResult>> results,
+    base::Time now,
+    base::TimeTicks now_ticks) {
+  std::unique_ptr<HostResolverInternalResult> data_result;
+  std::unique_ptr<HostResolverInternalResult> metadata_result;
+  std::unique_ptr<HostResolverInternalResult> error_result;
+  std::vector<std::unique_ptr<HostResolverInternalResult>> alias_results;
+
+  absl::optional<base::TimeDelta> smallest_ttl;
+  absl::optional<Source> source;
+  for (auto& result : results) {
+    if (result->expiration().has_value()) {
+      smallest_ttl = std::min(smallest_ttl.value_or(base::TimeDelta::Max()),
+                              result->expiration().value() - now_ticks);
+    }
+    if (result->timed_expiration().has_value()) {
+      smallest_ttl = std::min(smallest_ttl.value_or(base::TimeDelta::Max()),
+                              result->timed_expiration().value() - now);
+    }
+
+    Source result_source;
+    switch (result->source()) {
+      case HostResolverInternalResult::Source::kDns:
+        result_source = SOURCE_DNS;
+        break;
+      case HostResolverInternalResult::Source::kHosts:
+        result_source = SOURCE_HOSTS;
+        break;
+      case HostResolverInternalResult::Source::kUnknown:
+        result_source = SOURCE_UNKNOWN;
+        break;
+    }
+
+    switch (result->type()) {
+      case HostResolverInternalResult::Type::kData:
+        DCHECK(!data_result);  // Expect at most one data result.
+        data_result = std::move(result);
+        break;
+      case HostResolverInternalResult::Type::kMetadata:
+        DCHECK(!metadata_result);  // Expect at most one metadata result.
+        metadata_result = std::move(result);
+        break;
+      case HostResolverInternalResult::Type::kError:
+        DCHECK(!error_result);  // Expect at most one error result.
+        error_result = std::move(result);
+        break;
+      case HostResolverInternalResult::Type::kAlias:
+        alias_results.push_back(std::move(result));
+        break;
+    }
+
+    // Expect all results to have the same source.
+    DCHECK(!source.has_value() || source.value() == result_source);
+    source = result_source;
+  }
+
+  ttl_ = smallest_ttl.value_or(kUnknownTtl);
+  source_ = source.value_or(SOURCE_UNKNOWN);
+
+  if (error_result) {
+    DCHECK(!data_result);
+    DCHECK(!metadata_result);
+
+    error_ = error_result->AsError().error();
+
+    // For error results, should not create entry with a TTL unless it is a
+    // cacheable error.
+    if (!error_result->expiration().has_value() &&
+        !error_result->timed_expiration().has_value()) {
+      ttl_ = kUnknownTtl;
+    }
+  } else if (!data_result && !metadata_result) {
+    // Only alias results (or completely empty results). Never cacheable due to
+    // being equivalent to an error result without TTL.
+    error_ = ERR_NAME_NOT_RESOLVED;
+    ttl_ = kUnknownTtl;
+  } else {
+    error_ = OK;
+  }
+
+  if (data_result) {
+    DCHECK(!error_result);
+    DCHECK(!data_result->AsData().endpoints().empty() ||
+           !data_result->AsData().strings().empty() ||
+           !data_result->AsData().hosts().empty());
+    // Data results should always be cacheable.
+    DCHECK(data_result->expiration().has_value() ||
+           data_result->timed_expiration().has_value());
+
+    ip_endpoints_ = data_result->AsData().endpoints();
+    text_records_ = data_result->AsData().strings();
+    hostnames_ = data_result->AsData().hosts();
+    canonical_names_ = {data_result->domain_name()};
+
+    aliases_.emplace();
+    for (const auto& alias_result : alias_results) {
+      aliases_.value().insert(alias_result->domain_name());
+      aliases_.value().insert(alias_result->AsAlias().alias_target());
+    }
+    aliases_.value().insert(data_result->domain_name());
+  }
+  if (metadata_result) {
+    DCHECK(!error_result);
+    // Metadata results should always be cacheable.
+    DCHECK(metadata_result->expiration().has_value() ||
+           metadata_result->timed_expiration().has_value());
+
+    endpoint_metadatas_ = metadata_result->AsMetadata().metadatas();
+
+    // Even if otherwise empty, having the metadata result object signifies
+    // receiving a compatible HTTPS record.
+    https_record_compatibility_ = {true};
+
+    if (endpoint_metadatas_.value().empty())
+      error_ = ERR_NAME_NOT_RESOLVED;
+  }
+}
+
 HostCache::Entry::Entry(const Entry& entry) = default;
 
 HostCache::Entry::Entry(Entry&& entry) = default;
@@ -412,7 +533,7 @@
       ip_endpoints_(std::move(ip_endpoints)),
       aliases_(std::move(aliases)),
       source_(source),
-      ttl_(ttl ? ttl.value() : base::Seconds(-1)) {
+      ttl_(ttl ? ttl.value() : kUnknownTtl) {
   DCHECK(!ttl || ttl.value() >= base::TimeDelta());
 }
 
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index 7e1fcd97c..f077893d 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -47,6 +47,8 @@
 
 namespace net {
 
+class HostResolverInternalResult;
+
 // Cache used by HostResolver to map hostnames to their resolved result.
 class NET_EXPORT HostCache {
  public:
@@ -131,7 +133,7 @@
           absl::optional<base::TimeDelta> ttl)
         : error_(error),
           source_(source),
-          ttl_(ttl ? ttl.value() : base::Seconds(-1)) {
+          ttl_(ttl ? ttl.value() : kUnknownTtl) {
       DCHECK(!ttl || ttl.value() >= base::TimeDelta());
       SetResult(std::forward<T>(results));
     }
@@ -153,6 +155,12 @@
           Source source,
           absl::optional<base::TimeDelta> ttl = absl::nullopt);
 
+    // Adaptor to construct from HostResolverInternalResults. Only supports
+    // results extracted from a single DnsTransaction.
+    Entry(std::vector<std::unique_ptr<HostResolverInternalResult>> results,
+          base::Time now,
+          base::TimeTicks now_ticks);
+
     Entry(const Entry& entry);
     Entry(Entry&& entry);
     ~Entry();
@@ -267,6 +275,8 @@
 
     friend class HostCache;
 
+    static constexpr base::TimeDelta kUnknownTtl = base::Seconds(-1);
+
     Entry(const Entry& entry,
           base::TimeTicks now,
           base::TimeDelta ttl,
@@ -349,7 +359,7 @@
     absl::optional<std::set<std::string>> canonical_names_;
 
     // TTL obtained from the nameserver. Negative if unknown.
-    base::TimeDelta ttl_ = base::Seconds(-1);
+    base::TimeDelta ttl_ = kUnknownTtl;
 
     base::TimeTicks expires_;
     // Copied from the cache's network_changes_ when the entry is set; can
diff --git a/net/dns/host_cache_unittest.cc b/net/dns/host_cache_unittest.cc
index a19b72d..1b15ed0 100644
--- a/net/dns/host_cache_unittest.cc
+++ b/net/dns/host_cache_unittest.cc
@@ -6,8 +6,10 @@
 
 #include <algorithm>
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -23,9 +25,12 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/base/connection_endpoint_metadata.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/network_anonymization_key.h"
 #include "net/base/schemeful_site.h"
+#include "net/dns/host_resolver_internal_result.h"
 #include "net/dns/host_resolver_results_test_util.h"
 #include "net/dns/https_record_rdata.h"
 #include "net/dns/public/host_resolver_results.h"
@@ -2485,4 +2490,203 @@
   EXPECT_THAT(*result.aliases(), UnorderedElementsAre("name2"));
 }
 
+TEST(HostCacheTest, ConvertFromInternalAddressResult) {
+  const std::vector<IPEndPoint> kEndpoints{
+      IPEndPoint(IPAddress(2, 2, 2, 2), 46)};
+  constexpr base::TimeDelta kTtl1 = base::Minutes(45);
+  constexpr base::TimeDelta kTtl2 = base::Minutes(40);
+  constexpr base::TimeDelta kTtl3 = base::Minutes(55);
+
+  std::vector<std::unique_ptr<HostResolverInternalResult>> results;
+  results.push_back(std::make_unique<HostResolverInternalDataResult>(
+      "endpoint.test", DnsQueryType::AAAA, base::TimeTicks() + kTtl1,
+      base::Time() + kTtl1, HostResolverInternalResult::Source::kDns,
+      kEndpoints, std::vector<std::string>{}, std::vector<HostPortPair>{}));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain1.test", DnsQueryType::AAAA, base::TimeTicks() + kTtl2,
+      base::Time() + kTtl2, HostResolverInternalResult::Source::kDns,
+      "domain2.test"));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain2.test", DnsQueryType::AAAA, base::TimeTicks() + kTtl3,
+      base::Time() + kTtl3, HostResolverInternalResult::Source::kDns,
+      "endpoint.test"));
+
+  HostCache::Entry converted(std::move(results), base::Time(),
+                             base::TimeTicks());
+
+  // Expect kTtl2 because it is the min TTL.
+  HostCache::Entry expected(
+      OK, kEndpoints,
+      /*aliases=*/{"domain1.test", "domain2.test", "endpoint.test"},
+      HostCache::Entry::SOURCE_DNS, kTtl2);
+  expected.set_canonical_names(std::set<std::string>{"endpoint.test"});
+
+  // Entries converted from HostResolverInternalDataResults do not differentiate
+  // between empty and no-data for the various data types, so need to set empty
+  // strings and hostname entries into `expected`.
+  expected.set_text_records(std::vector<std::string>());
+  expected.set_hostnames(std::vector<HostPortPair>());
+
+  EXPECT_EQ(converted, expected);
+}
+
+TEST(HostCacheTest, ConvertFromInternalMetadataResult) {
+  const std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+      kMetadatas{{1, ConnectionEndpointMetadata({"h2", "h3"},
+                                                /*ech_config_list=*/{},
+                                                "target.test")}};
+  constexpr base::TimeDelta kTtl1 = base::Minutes(45);
+  constexpr base::TimeDelta kTtl2 = base::Minutes(40);
+  constexpr base::TimeDelta kTtl3 = base::Minutes(55);
+
+  std::vector<std::unique_ptr<HostResolverInternalResult>> results;
+  results.push_back(std::make_unique<HostResolverInternalMetadataResult>(
+      "endpoint.test", DnsQueryType::HTTPS, base::TimeTicks() + kTtl1,
+      base::Time() + kTtl1, HostResolverInternalResult::Source::kDns,
+      kMetadatas));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain1.test", DnsQueryType::HTTPS, base::TimeTicks() + kTtl2,
+      base::Time() + kTtl2, HostResolverInternalResult::Source::kDns,
+      "domain2.test"));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain2.test", DnsQueryType::HTTPS, base::TimeTicks() + kTtl3,
+      base::Time() + kTtl3, HostResolverInternalResult::Source::kDns,
+      "endpoint.test"));
+
+  HostCache::Entry converted(std::move(results), base::Time(),
+                             base::TimeTicks());
+
+  // Expect kTtl2 because it is the min TTL.
+  HostCache::Entry expected(OK, kMetadatas, HostCache::Entry::SOURCE_DNS,
+                            kTtl2);
+  expected.set_https_record_compatibility(std::vector<bool>{true});
+
+  EXPECT_EQ(converted, expected);
+}
+
+// Test the case of compatible HTTPS records but no metadata of use to Chrome.
+// Represented in internal result type as an empty metadata result. Represented
+// in HostCache::Entry as empty metadata with at least one true in
+// `https_record_compatibility_`.
+TEST(HostCacheTest, ConvertFromCompatibleOnlyInternalMetadataResult) {
+  const std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+      kMetadatas;
+  constexpr base::TimeDelta kTtl1 = base::Minutes(45);
+  constexpr base::TimeDelta kTtl2 = base::Minutes(40);
+  constexpr base::TimeDelta kTtl3 = base::Minutes(55);
+
+  std::vector<std::unique_ptr<HostResolverInternalResult>> results;
+  results.push_back(std::make_unique<HostResolverInternalMetadataResult>(
+      "endpoint.test", DnsQueryType::HTTPS, base::TimeTicks() + kTtl1,
+      base::Time() + kTtl1, HostResolverInternalResult::Source::kDns,
+      kMetadatas));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain1.test", DnsQueryType::HTTPS, base::TimeTicks() + kTtl2,
+      base::Time() + kTtl2, HostResolverInternalResult::Source::kDns,
+      "domain2.test"));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain2.test", DnsQueryType::HTTPS, base::TimeTicks() + kTtl3,
+      base::Time() + kTtl3, HostResolverInternalResult::Source::kDns,
+      "endpoint.test"));
+
+  HostCache::Entry converted(std::move(results), base::Time(),
+                             base::TimeTicks());
+
+  // Expect kTtl2 because it is the min TTL.
+  HostCache::Entry expected(ERR_NAME_NOT_RESOLVED, kMetadatas,
+                            HostCache::Entry::SOURCE_DNS, kTtl2);
+  expected.set_https_record_compatibility(std::vector<bool>{true});
+
+  EXPECT_EQ(converted, expected);
+}
+
+TEST(HostCacheTest, ConvertFromInternalErrorResult) {
+  constexpr base::TimeDelta kTtl1 = base::Minutes(45);
+  constexpr base::TimeDelta kTtl2 = base::Minutes(40);
+  constexpr base::TimeDelta kTtl3 = base::Minutes(55);
+
+  std::vector<std::unique_ptr<HostResolverInternalResult>> results;
+  results.push_back(std::make_unique<HostResolverInternalErrorResult>(
+      "endpoint.test", DnsQueryType::A, base::TimeTicks() + kTtl1,
+      base::Time() + kTtl1, HostResolverInternalResult::Source::kDns,
+      ERR_NAME_NOT_RESOLVED));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain1.test", DnsQueryType::A, base::TimeTicks() + kTtl2,
+      base::Time() + kTtl2, HostResolverInternalResult::Source::kDns,
+      "domain2.test"));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain2.test", DnsQueryType::A, base::TimeTicks() + kTtl3,
+      base::Time() + kTtl3, HostResolverInternalResult::Source::kDns,
+      "endpoint.test"));
+
+  HostCache::Entry converted(std::move(results), base::Time(),
+                             base::TimeTicks());
+
+  // Expect kTtl2 because it is the min TTL.
+  HostCache::Entry expected(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS,
+                            kTtl2);
+
+  EXPECT_EQ(converted, expected);
+}
+
+TEST(HostCacheTest, ConvertFromNonCachableInternalErrorResult) {
+  constexpr base::TimeDelta kTtl1 = base::Minutes(45);
+  constexpr base::TimeDelta kTtl2 = base::Minutes(40);
+
+  std::vector<std::unique_ptr<HostResolverInternalResult>> results;
+  results.push_back(std::make_unique<HostResolverInternalErrorResult>(
+      "endpoint.test", DnsQueryType::AAAA, /*expiration=*/absl::nullopt,
+      /*timed_expiration=*/absl::nullopt,
+      HostResolverInternalResult::Source::kDns, ERR_NAME_NOT_RESOLVED));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain1.test", DnsQueryType::AAAA, base::TimeTicks() + kTtl1,
+      base::Time() + kTtl1, HostResolverInternalResult::Source::kDns,
+      "domain2.test"));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain2.test", DnsQueryType::AAAA, base::TimeTicks() + kTtl2,
+      base::Time() + kTtl2, HostResolverInternalResult::Source::kDns,
+      "endpoint.test"));
+
+  HostCache::Entry converted(std::move(results), base::Time(),
+                             base::TimeTicks());
+
+  // Expect no TTL because error is non-cachable (has no TTL itself).
+  HostCache::Entry expected(ERR_NAME_NOT_RESOLVED,
+                            HostCache::Entry::SOURCE_DNS);
+
+  EXPECT_EQ(converted, expected);
+}
+
+TEST(HostCacheTest, ConvertFromInternalAliasOnlyResult) {
+  constexpr base::TimeDelta kTtl1 = base::Minutes(45);
+  constexpr base::TimeDelta kTtl2 = base::Minutes(40);
+
+  std::vector<std::unique_ptr<HostResolverInternalResult>> results;
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain1.test", DnsQueryType::A, base::TimeTicks() + kTtl1,
+      base::Time() + kTtl1, HostResolverInternalResult::Source::kDns,
+      "domain2.test"));
+  results.push_back(std::make_unique<HostResolverInternalAliasResult>(
+      "domain2.test", DnsQueryType::A, base::TimeTicks() + kTtl2,
+      base::Time() + kTtl2, HostResolverInternalResult::Source::kDns,
+      "endpoint.test"));
+
+  HostCache::Entry converted(std::move(results), base::Time(),
+                             base::TimeTicks());
+
+  // Expect no TTL because alias-only results are not cacheable.
+  HostCache::Entry expected(ERR_NAME_NOT_RESOLVED,
+                            HostCache::Entry::SOURCE_DNS);
+
+  EXPECT_EQ(converted, expected);
+}
+
+TEST(HostCacheTest, ConvertFromEmptyInternalResult) {
+  HostCache::Entry converted({}, base::Time(), base::TimeTicks());
+  HostCache::Entry expected(ERR_NAME_NOT_RESOLVED,
+                            HostCache::Entry::SOURCE_UNKNOWN);
+
+  EXPECT_EQ(converted, expected);
+}
+
 }  // namespace net
diff --git a/net/dns/host_resolver_internal_result.cc b/net/dns/host_resolver_internal_result.cc
new file mode 100644
index 0000000..487bf81
--- /dev/null
+++ b/net/dns/host_resolver_internal_result.cc
@@ -0,0 +1,586 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/host_resolver_internal_result.h"
+
+#include <map>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/check_op.h"
+#include "base/json/values_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "net/base/connection_endpoint_metadata.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/dns/https_record_rdata.h"
+#include "net/dns/public/dns_query_type.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/url_canon.h"
+#include "url/url_canon_stdstring.h"
+
+namespace net {
+
+namespace {
+
+// base::Value keys
+constexpr base::StringPiece kValueDomainNameKey = "domain_name";
+constexpr base::StringPiece kValueQueryTypeKey = "query_type";
+constexpr base::StringPiece kValueTypeKey = "type";
+constexpr base::StringPiece kValueSourceKey = "source";
+constexpr base::StringPiece kValueTimedExpirationKey = "timed_expiration";
+constexpr base::StringPiece kValueEndpointsKey = "endpoints";
+constexpr base::StringPiece kValueStringsKey = "strings";
+constexpr base::StringPiece kValueHostsKey = "hosts";
+constexpr base::StringPiece kValueMetadatasKey = "metadatas";
+constexpr base::StringPiece kValueMetadataWeightKey = "metadata_weight";
+constexpr base::StringPiece kValueMetadataValueKey = "metadata_value";
+constexpr base::StringPiece kValueErrorKey = "error";
+constexpr base::StringPiece kValueAliasTargetKey = "alias_target";
+
+// Returns `domain_name` as-is if it could not be canonicalized.
+std::string MaybeCanonicalizeName(std::string domain_name) {
+  std::string canonicalized;
+  url::StdStringCanonOutput output(&canonicalized);
+  url::CanonHostInfo host_info;
+
+  url::CanonicalizeHostVerbose(domain_name.data(),
+                               url::Component(0, domain_name.size()), &output,
+                               &host_info);
+
+  if (host_info.family == url::CanonHostInfo::Family::NEUTRAL) {
+    output.Complete();
+    return canonicalized;
+  } else {
+    return domain_name;
+  }
+}
+
+base::Value EndpointMetadataPairToValue(
+    const std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>& pair) {
+  base::Value::Dict dictionary;
+  dictionary.Set(kValueMetadataWeightKey, pair.first);
+  dictionary.Set(kValueMetadataValueKey, pair.second.ToValue());
+  return base::Value(std::move(dictionary));
+}
+
+absl::optional<std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
+EndpointMetadataPairFromValue(const base::Value& value) {
+  const base::Value::Dict* dict = value.GetIfDict();
+  if (!dict)
+    return absl::nullopt;
+
+  absl::optional<int> weight = dict->FindInt(kValueMetadataWeightKey);
+  if (!weight || !base::IsValueInRangeForNumericType<HttpsRecordPriority>(
+                     weight.value())) {
+    return absl::nullopt;
+  }
+
+  const base::Value* metadata_value = dict->Find(kValueMetadataValueKey);
+  if (!metadata_value)
+    return absl::nullopt;
+  absl::optional<ConnectionEndpointMetadata> metadata =
+      ConnectionEndpointMetadata::FromValue(*metadata_value);
+  if (!metadata)
+    return absl::nullopt;
+
+  return std::make_pair(base::checked_cast<HttpsRecordPriority>(weight.value()),
+                        std::move(metadata).value());
+}
+
+absl::optional<DnsQueryType> QueryTypeFromValue(const base::Value& value) {
+  const std::string* query_type_string = value.GetIfString();
+  if (!query_type_string)
+    return absl::nullopt;
+  const auto* query_type_it =
+      base::ranges::find(kDnsQueryTypes, *query_type_string,
+                         &decltype(kDnsQueryTypes)::value_type::second);
+  if (query_type_it == kDnsQueryTypes.end())
+    return absl::nullopt;
+
+  return query_type_it->first;
+}
+
+base::Value TypeToValue(HostResolverInternalResult::Type type) {
+  switch (type) {
+    case HostResolverInternalResult::Type::kData:
+      return base::Value("data");
+    case HostResolverInternalResult::Type::kMetadata:
+      return base::Value("metadata");
+    case HostResolverInternalResult::Type::kError:
+      return base::Value("error");
+    case HostResolverInternalResult::Type::kAlias:
+      return base::Value("alias");
+  }
+}
+
+absl::optional<HostResolverInternalResult::Type> TypeFromValue(
+    const base::Value& value) {
+  const std::string* string = value.GetIfString();
+  if (!string)
+    return absl::nullopt;
+
+  if (*string == "data") {
+    return HostResolverInternalResult::Type::kData;
+  } else if (*string == "metadata") {
+    return HostResolverInternalResult::Type::kMetadata;
+  } else if (*string == "error") {
+    return HostResolverInternalResult::Type::kError;
+  } else if (*string == "alias") {
+    return HostResolverInternalResult::Type::kAlias;
+  } else {
+    return absl::nullopt;
+  }
+}
+
+base::Value SourceToValue(HostResolverInternalResult::Source source) {
+  switch (source) {
+    case HostResolverInternalResult::Source::kDns:
+      return base::Value("dns");
+    case HostResolverInternalResult::Source::kHosts:
+      return base::Value("hosts");
+    case HostResolverInternalResult::Source::kUnknown:
+      return base::Value("unknown");
+  }
+}
+
+absl::optional<HostResolverInternalResult::Source> SourceFromValue(
+    const base::Value& value) {
+  const std::string* string = value.GetIfString();
+  if (!string)
+    return absl::nullopt;
+
+  if (*string == "dns") {
+    return HostResolverInternalResult::Source::kDns;
+  } else if (*string == "hosts") {
+    return HostResolverInternalResult::Source::kHosts;
+  } else if (*string == "unknown") {
+    return HostResolverInternalResult::Source::kUnknown;
+  } else {
+    return absl::nullopt;
+  }
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<HostResolverInternalResult>
+HostResolverInternalResult::FromValue(const base::Value& value) {
+  const base::Value::Dict* dict = value.GetIfDict();
+  if (!dict)
+    return nullptr;
+
+  const base::Value* type_value = dict->Find(kValueTypeKey);
+  if (!type_value)
+    return nullptr;
+  absl::optional<Type> type = TypeFromValue(*type_value);
+  if (!type.has_value())
+    return nullptr;
+
+  switch (type.value()) {
+    case Type::kData:
+      return HostResolverInternalDataResult::FromValue(value);
+    case Type::kMetadata:
+      return HostResolverInternalMetadataResult::FromValue(value);
+    case Type::kError:
+      return HostResolverInternalErrorResult::FromValue(value);
+    case Type::kAlias:
+      return HostResolverInternalAliasResult::FromValue(value);
+  }
+}
+
+const HostResolverInternalDataResult& HostResolverInternalResult::AsData()
+    const {
+  CHECK_EQ(type_, Type::kData);
+  return *static_cast<const HostResolverInternalDataResult*>(this);
+}
+
+const HostResolverInternalMetadataResult&
+HostResolverInternalResult::AsMetadata() const {
+  CHECK_EQ(type_, Type::kMetadata);
+  return *static_cast<const HostResolverInternalMetadataResult*>(this);
+}
+
+const HostResolverInternalErrorResult& HostResolverInternalResult::AsError()
+    const {
+  CHECK_EQ(type_, Type::kError);
+  return *static_cast<const HostResolverInternalErrorResult*>(this);
+}
+
+const HostResolverInternalAliasResult& HostResolverInternalResult::AsAlias()
+    const {
+  CHECK_EQ(type_, Type::kAlias);
+  return *static_cast<const HostResolverInternalAliasResult*>(this);
+}
+
+HostResolverInternalResult::HostResolverInternalResult(
+    std::string domain_name,
+    DnsQueryType query_type,
+    absl::optional<base::TimeTicks> expiration,
+    absl::optional<base::Time> timed_expiration,
+    Type type,
+    Source source)
+    : domain_name_(MaybeCanonicalizeName(std::move(domain_name))),
+      query_type_(query_type),
+      type_(type),
+      source_(source),
+      expiration_(expiration),
+      timed_expiration_(timed_expiration) {
+  DCHECK(!domain_name_.empty());
+  // If `expiration` has a value, `timed_expiration` must too.
+  DCHECK(!expiration_.has_value() || timed_expiration.has_value());
+}
+
+HostResolverInternalResult::HostResolverInternalResult(
+    const base::Value::Dict& dict)
+    : domain_name_(*dict.FindString(kValueDomainNameKey)),
+      query_type_(QueryTypeFromValue(*dict.Find(kValueQueryTypeKey)).value()),
+      type_(TypeFromValue(*dict.Find(kValueTypeKey)).value()),
+      source_(SourceFromValue(*dict.Find(kValueSourceKey)).value()),
+      timed_expiration_(
+          dict.contains(kValueTimedExpirationKey)
+              ? base::ValueToTime(*dict.Find(kValueTimedExpirationKey))
+              : absl::optional<base::Time>()) {}
+
+// static
+bool HostResolverInternalResult::ValidateValueBaseDict(
+    const base::Value::Dict& dict,
+    bool require_timed_expiration) {
+  const std::string* domain_name = dict.FindString(kValueDomainNameKey);
+  if (!domain_name)
+    return false;
+
+  const std::string* query_type_string = dict.FindString(kValueQueryTypeKey);
+  if (!query_type_string)
+    return false;
+  const auto* query_type_it =
+      base::ranges::find(kDnsQueryTypes, *query_type_string,
+                         &decltype(kDnsQueryTypes)::value_type::second);
+  if (query_type_it == kDnsQueryTypes.end())
+    return false;
+
+  const base::Value* type_value = dict.Find(kValueTypeKey);
+  if (!type_value)
+    return false;
+  absl::optional<Type> type = TypeFromValue(*type_value);
+  if (!type.has_value())
+    return false;
+
+  const base::Value* source_value = dict.Find(kValueSourceKey);
+  if (!source_value)
+    return false;
+  absl::optional<Source> source = SourceFromValue(*source_value);
+  if (!source.has_value())
+    return false;
+
+  absl::optional<base::Time> timed_expiration;
+  const base::Value* timed_expiration_value =
+      dict.Find(kValueTimedExpirationKey);
+  if (require_timed_expiration && !timed_expiration_value)
+    return false;
+  if (timed_expiration_value) {
+    timed_expiration = base::ValueToTime(timed_expiration_value);
+    if (!timed_expiration.has_value())
+      return false;
+  }
+
+  return true;
+}
+
+base::Value::Dict HostResolverInternalResult::ToValueBaseDict() const {
+  base::Value::Dict dict;
+
+  dict.Set(kValueDomainNameKey, domain_name_);
+  dict.Set(kValueQueryTypeKey, kDnsQueryTypes.at(query_type_));
+  dict.Set(kValueTypeKey, TypeToValue(type_));
+  dict.Set(kValueSourceKey, SourceToValue(source_));
+
+  // `expiration_` is not serialized because it is TimeTicks.
+
+  if (timed_expiration_.has_value()) {
+    dict.Set(kValueTimedExpirationKey,
+             base::TimeToValue(timed_expiration_.value()));
+  }
+
+  return dict;
+}
+
+// static
+std::unique_ptr<HostResolverInternalDataResult>
+HostResolverInternalDataResult::FromValue(const base::Value& value) {
+  const base::Value::Dict* dict = value.GetIfDict();
+  if (!dict || !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/true))
+    return nullptr;
+
+  const base::Value::List* endpoint_values = dict->FindList(kValueEndpointsKey);
+  if (!endpoint_values)
+    return nullptr;
+
+  std::vector<IPEndPoint> endpoints;
+  endpoints.reserve(endpoint_values->size());
+  for (const base::Value& endpoint_value : *endpoint_values) {
+    absl::optional<IPEndPoint> endpoint = IPEndPoint::FromValue(endpoint_value);
+    if (!endpoint.has_value())
+      return nullptr;
+
+    endpoints.push_back(std::move(endpoint).value());
+  }
+
+  const base::Value::List* string_values = dict->FindList(kValueStringsKey);
+  if (!string_values)
+    return nullptr;
+
+  std::vector<std::string> strings;
+  strings.reserve(string_values->size());
+  for (const base::Value& string_value : *string_values) {
+    const std::string* string = string_value.GetIfString();
+    if (!string)
+      return nullptr;
+
+    strings.push_back(*string);
+  }
+
+  const base::Value::List* host_values = dict->FindList(kValueHostsKey);
+  if (!host_values)
+    return nullptr;
+
+  std::vector<HostPortPair> hosts;
+  hosts.reserve(host_values->size());
+  for (const base::Value& host_value : *host_values) {
+    absl::optional<HostPortPair> host = HostPortPair::FromValue(host_value);
+    if (!host.has_value())
+      return nullptr;
+
+    hosts.push_back(std::move(host).value());
+  }
+
+  // WrapUnique due to private constructor.
+  return base::WrapUnique(new HostResolverInternalDataResult(
+      *dict, std::move(endpoints), std::move(strings), std::move(hosts)));
+}
+
+HostResolverInternalDataResult::HostResolverInternalDataResult(
+    std::string domain_name,
+    DnsQueryType query_type,
+    absl::optional<base::TimeTicks> expiration,
+    base::Time timed_expiration,
+    Source source,
+    std::vector<IPEndPoint> endpoints,
+    std::vector<std::string> strings,
+    std::vector<HostPortPair> hosts)
+    : HostResolverInternalResult(std::move(domain_name),
+                                 query_type,
+                                 expiration,
+                                 timed_expiration,
+                                 Type::kData,
+                                 source),
+      endpoints_(std::move(endpoints)),
+      strings_(std::move(strings)),
+      hosts_(std::move(hosts)) {
+  DCHECK(!endpoints_.empty() || !strings_.empty() || !hosts_.empty());
+}
+
+HostResolverInternalDataResult::~HostResolverInternalDataResult() = default;
+
+base::Value HostResolverInternalDataResult::ToValue() const {
+  base::Value::Dict dict = ToValueBaseDict();
+
+  base::Value::List endpoints_list;
+  endpoints_list.reserve(endpoints_.size());
+  for (IPEndPoint endpoint : endpoints_) {
+    endpoints_list.Append(endpoint.ToValue());
+  }
+  dict.Set(kValueEndpointsKey, std::move(endpoints_list));
+
+  base::Value::List strings_list;
+  strings_list.reserve(strings_.size());
+  for (const std::string& string : strings_) {
+    strings_list.Append(string);
+  }
+  dict.Set(kValueStringsKey, std::move(strings_list));
+
+  base::Value::List hosts_list;
+  hosts_list.reserve(hosts_.size());
+  for (const HostPortPair& host : hosts_) {
+    hosts_list.Append(host.ToValue());
+  }
+  dict.Set(kValueHostsKey, std::move(hosts_list));
+
+  return base::Value(std::move(dict));
+}
+
+HostResolverInternalDataResult::HostResolverInternalDataResult(
+    const base::Value::Dict& dict,
+    std::vector<IPEndPoint> endpoints,
+    std::vector<std::string> strings,
+    std::vector<HostPortPair> hosts)
+    : HostResolverInternalResult(dict),
+      endpoints_(std::move(endpoints)),
+      strings_(std::move(strings)),
+      hosts_(std::move(hosts)) {}
+
+// static
+std::unique_ptr<HostResolverInternalMetadataResult>
+HostResolverInternalMetadataResult::FromValue(const base::Value& value) {
+  const base::Value::Dict* dict = value.GetIfDict();
+  if (!dict || !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/true))
+    return nullptr;
+
+  const base::Value::List* metadata_values = dict->FindList(kValueMetadatasKey);
+  if (!metadata_values)
+    return nullptr;
+
+  std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas;
+  for (const base::Value& metadata_value : *metadata_values) {
+    absl::optional<std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
+        metadata = EndpointMetadataPairFromValue(metadata_value);
+    if (!metadata.has_value())
+      return nullptr;
+    metadatas.insert(std::move(metadata).value());
+  }
+
+  // WrapUnique due to private constructor.
+  return base::WrapUnique(
+      new HostResolverInternalMetadataResult(*dict, std::move(metadatas)));
+}
+
+HostResolverInternalMetadataResult::HostResolverInternalMetadataResult(
+    std::string domain_name,
+    DnsQueryType query_type,
+    absl::optional<base::TimeTicks> expiration,
+    base::Time timed_expiration,
+    Source source,
+    std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas)
+    : HostResolverInternalResult(std::move(domain_name),
+                                 query_type,
+                                 expiration,
+                                 timed_expiration,
+                                 Type::kMetadata,
+                                 source),
+      metadatas_(std::move(metadatas)) {}
+
+HostResolverInternalMetadataResult::~HostResolverInternalMetadataResult() =
+    default;
+
+base::Value HostResolverInternalMetadataResult::ToValue() const {
+  base::Value::Dict dict = ToValueBaseDict();
+
+  base::Value::List metadatas_list;
+  metadatas_list.reserve(metadatas_.size());
+  for (const std::pair<const HttpsRecordPriority, ConnectionEndpointMetadata>&
+           metadata_pair : metadatas_) {
+    metadatas_list.Append(EndpointMetadataPairToValue(metadata_pair));
+  }
+  dict.Set(kValueMetadatasKey, std::move(metadatas_list));
+
+  return base::Value(std::move(dict));
+}
+
+HostResolverInternalMetadataResult::HostResolverInternalMetadataResult(
+    const base::Value::Dict& dict,
+    std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas)
+    : HostResolverInternalResult(dict), metadatas_(std::move(metadatas)) {}
+
+// static
+std::unique_ptr<HostResolverInternalErrorResult>
+HostResolverInternalErrorResult::FromValue(const base::Value& value) {
+  const base::Value::Dict* dict = value.GetIfDict();
+  if (!dict ||
+      !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/false)) {
+    return nullptr;
+  }
+
+  absl::optional<int> error = dict->FindInt(kValueErrorKey);
+  if (!error.has_value())
+    return nullptr;
+
+  // WrapUnique due to private constructor.
+  return base::WrapUnique(
+      new HostResolverInternalErrorResult(*dict, error.value()));
+}
+
+HostResolverInternalErrorResult::HostResolverInternalErrorResult(
+    std::string domain_name,
+    DnsQueryType query_type,
+    absl::optional<base::TimeTicks> expiration,
+    absl::optional<base::Time> timed_expiration,
+    Source source,
+    int error)
+    : HostResolverInternalResult(std::move(domain_name),
+                                 query_type,
+                                 expiration,
+                                 timed_expiration,
+                                 Type::kError,
+                                 source),
+      error_(error) {}
+
+base::Value HostResolverInternalErrorResult::ToValue() const {
+  base::Value::Dict dict = ToValueBaseDict();
+
+  dict.Set(kValueErrorKey, error_);
+
+  return base::Value(std::move(dict));
+}
+
+HostResolverInternalErrorResult::HostResolverInternalErrorResult(
+    const base::Value::Dict& dict,
+    int error)
+    : HostResolverInternalResult(dict), error_(error) {
+  DCHECK_NE(error_, OK);
+}
+
+// static
+std::unique_ptr<HostResolverInternalAliasResult>
+HostResolverInternalAliasResult::FromValue(const base::Value& value) {
+  const base::Value::Dict* dict = value.GetIfDict();
+  if (!dict || !ValidateValueBaseDict(*dict, /*require_timed_expiration=*/true))
+    return nullptr;
+
+  const std::string* target = dict->FindString(kValueAliasTargetKey);
+  if (!target)
+    return nullptr;
+
+  // WrapUnique due to private constructor.
+  return base::WrapUnique(new HostResolverInternalAliasResult(*dict, *target));
+}
+
+HostResolverInternalAliasResult::HostResolverInternalAliasResult(
+    std::string domain_name,
+    DnsQueryType query_type,
+    absl::optional<base::TimeTicks> expiration,
+    base::Time timed_expiration,
+    Source source,
+    std::string alias_target)
+    : HostResolverInternalResult(std::move(domain_name),
+                                 query_type,
+                                 expiration,
+                                 timed_expiration,
+                                 Type::kAlias,
+                                 source),
+      alias_target_(MaybeCanonicalizeName(std::move(alias_target))) {
+  DCHECK(!alias_target_.empty());
+}
+
+base::Value HostResolverInternalAliasResult::ToValue() const {
+  base::Value::Dict dict = ToValueBaseDict();
+
+  dict.Set(kValueAliasTargetKey, alias_target_);
+
+  return base::Value(std::move(dict));
+}
+
+HostResolverInternalAliasResult::HostResolverInternalAliasResult(
+    const base::Value::Dict& dict,
+    std::string alias_target)
+    : HostResolverInternalResult(dict),
+      alias_target_(MaybeCanonicalizeName(std::move(alias_target))) {}
+
+}  // namespace net
diff --git a/net/dns/host_resolver_internal_result.h b/net/dns/host_resolver_internal_result.h
new file mode 100644
index 0000000..cf3ec725
--- /dev/null
+++ b/net/dns/host_resolver_internal_result.h
@@ -0,0 +1,268 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_HOST_RESOLVER_INTERNAL_RESULT_H_
+#define NET_DNS_HOST_RESOLVER_INTERNAL_RESULT_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/time/time.h"
+#include "base/values.h"
+#include "net/base/connection_endpoint_metadata.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+#include "net/dns/https_record_rdata.h"
+#include "net/dns/public/dns_query_type.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+class HostResolverInternalDataResult;
+class HostResolverInternalMetadataResult;
+class HostResolverInternalErrorResult;
+class HostResolverInternalAliasResult;
+
+// Parsed and extracted result type for use internally to HostResolver code.
+class NET_EXPORT_PRIVATE HostResolverInternalResult {
+ public:
+  enum class Type { kData, kMetadata, kError, kAlias };
+  enum class Source { kDns, kHosts, kUnknown };
+
+  // Nullptr if `value` is malformed to be deserialized.
+  static std::unique_ptr<HostResolverInternalResult> FromValue(
+      const base::Value& value);
+
+  virtual ~HostResolverInternalResult() = default;
+
+  const std::string& domain_name() const { return domain_name_; }
+  DnsQueryType query_type() const { return query_type_; }
+  Type type() const { return type_; }
+  Source source() const { return source_; }
+  absl::optional<base::TimeTicks> expiration() const { return expiration_; }
+  absl::optional<base::Time> timed_expiration() const {
+    return timed_expiration_;
+  }
+
+  const HostResolverInternalDataResult& AsData() const;
+  const HostResolverInternalMetadataResult& AsMetadata() const;
+  const HostResolverInternalErrorResult& AsError() const;
+  const HostResolverInternalAliasResult& AsAlias() const;
+
+  virtual base::Value ToValue() const = 0;
+
+ protected:
+  HostResolverInternalResult(std::string domain_name,
+                             DnsQueryType query_type,
+                             absl::optional<base::TimeTicks> expiration,
+                             absl::optional<base::Time> timed_expiration,
+                             Type type,
+                             Source source);
+  // Expect to only be called with a `dict` well-formed for deserialization. Can
+  // be checked via ValidateValueBaseDict().
+  explicit HostResolverInternalResult(const base::Value::Dict& dict);
+
+  bool operator==(const HostResolverInternalResult& other) const {
+    return std::tie(domain_name_, query_type_, type_, source_, expiration_,
+                    timed_expiration_) ==
+           std::tie(other.domain_name_, other.query_type_, other.type_,
+                    other.source_, other.expiration_, other.timed_expiration_);
+  }
+
+  static bool ValidateValueBaseDict(const base::Value::Dict& dict,
+                                    bool require_timed_expiration);
+  base::Value::Dict ToValueBaseDict() const;
+
+ private:
+  const std::string domain_name_;
+  const DnsQueryType query_type_;
+  const Type type_;
+  const Source source_;
+
+  // Expiration logic should prefer to be based on `expiration_` for correctness
+  // through system time changes. But if result has been serialized to disk, it
+  // may be that only `timed_expiration_` is available.
+  const absl::optional<base::TimeTicks> expiration_;
+  const absl::optional<base::Time> timed_expiration_;
+};
+
+// Parsed and extracted result containing result data.
+class NET_EXPORT_PRIVATE HostResolverInternalDataResult final
+    : public HostResolverInternalResult {
+ public:
+  static std::unique_ptr<HostResolverInternalDataResult> FromValue(
+      const base::Value& value);
+
+  // `domain_name` is dotted form.
+  HostResolverInternalDataResult(std::string domain_name,
+                                 DnsQueryType query_type,
+                                 absl::optional<base::TimeTicks> expiration,
+                                 base::Time timed_expiration,
+                                 Source source,
+                                 std::vector<IPEndPoint> endpoints,
+                                 std::vector<std::string> strings,
+                                 std::vector<HostPortPair> hosts);
+  ~HostResolverInternalDataResult() override;
+
+  HostResolverInternalDataResult(const HostResolverInternalDataResult&) =
+      delete;
+  HostResolverInternalDataResult& operator=(
+      const HostResolverInternalDataResult&) = delete;
+
+  bool operator==(const HostResolverInternalDataResult& other) const {
+    return HostResolverInternalResult::operator==(other) &&
+           std::tie(endpoints_, strings_, hosts_) ==
+               std::tie(other.endpoints_, other.strings_, other.hosts_);
+  }
+
+  const std::vector<IPEndPoint>& endpoints() const { return endpoints_; }
+  const std::vector<std::string>& strings() const { return strings_; }
+  const std::vector<HostPortPair>& hosts() const { return hosts_; }
+
+  base::Value ToValue() const override;
+
+ private:
+  HostResolverInternalDataResult(const base::Value::Dict& dict,
+                                 std::vector<IPEndPoint> endpoints,
+                                 std::vector<std::string> strings,
+                                 std::vector<HostPortPair> hosts);
+
+  // Corresponds to the `HostResolverEndpointResult::ip_endpoints` portion of
+  // `HostResolver::ResolveHostRequest::GetEndpointResults()`.
+  const std::vector<IPEndPoint> endpoints_;
+
+  // Corresponds to `HostResolver::ResolveHostRequest::GetTextResults()`.
+  const std::vector<std::string> strings_;
+
+  // Corresponds to `HostResolver::ResolveHostRequest::GetHostnameResults()`.
+  const std::vector<HostPortPair> hosts_;
+};
+
+// Parsed and extracted connection metadata, but not usable on its own without
+// being paired with separate HostResolverInternalDataResult data (for the
+// domain name specified by `ConnectionEndpointMetadata::target_name`). An empty
+// metadata result signifies that compatible HTTPS records were received but
+// with no contained metadata of use to Chrome.
+class NET_EXPORT_PRIVATE HostResolverInternalMetadataResult final
+    : public HostResolverInternalResult {
+ public:
+  static std::unique_ptr<HostResolverInternalMetadataResult> FromValue(
+      const base::Value& value);
+
+  // `domain_name` and `data_domain` are dotted form domain names.
+  HostResolverInternalMetadataResult(
+      std::string domain_name,
+      DnsQueryType query_type,
+      absl::optional<base::TimeTicks> expiration,
+      base::Time timed_expiration,
+      Source source,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas);
+  ~HostResolverInternalMetadataResult() override;
+
+  HostResolverInternalMetadataResult(
+      const HostResolverInternalMetadataResult&) = delete;
+  HostResolverInternalMetadataResult& operator=(
+      const HostResolverInternalMetadataResult&) = delete;
+
+  bool operator==(const HostResolverInternalMetadataResult& other) const {
+    return HostResolverInternalResult::operator==(other) &&
+           metadatas_ == other.metadatas_;
+  }
+
+  const std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>&
+  metadatas() const {
+    return metadatas_;
+  }
+
+  base::Value ToValue() const override;
+
+ private:
+  HostResolverInternalMetadataResult(
+      const base::Value::Dict& dict,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas);
+
+  std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas_;
+};
+
+// Parsed and extracted error.
+class NET_EXPORT_PRIVATE HostResolverInternalErrorResult final
+    : public HostResolverInternalResult {
+ public:
+  static std::unique_ptr<HostResolverInternalErrorResult> FromValue(
+      const base::Value& value);
+
+  // `domain_name` is dotted form. `timed_expiration` may be `nullopt` for
+  // non-cacheable errors.
+  HostResolverInternalErrorResult(std::string domain_name,
+                                  DnsQueryType query_type,
+                                  absl::optional<base::TimeTicks> expiration,
+                                  absl::optional<base::Time> timed_expiration,
+                                  Source source,
+                                  int error);
+  ~HostResolverInternalErrorResult() override = default;
+
+  HostResolverInternalErrorResult(const HostResolverInternalErrorResult&) =
+      delete;
+  HostResolverInternalErrorResult& operator=(
+      const HostResolverInternalErrorResult&) = delete;
+
+  bool operator==(const HostResolverInternalErrorResult& other) const {
+    return HostResolverInternalResult::operator==(other) &&
+           error_ == other.error_;
+  }
+
+  int error() const { return error_; }
+
+  base::Value ToValue() const override;
+
+ private:
+  HostResolverInternalErrorResult(const base::Value::Dict& dict, int error);
+
+  const int error_;
+};
+
+// Parsed and extracted alias (CNAME or alias-type HTTPS).
+class NET_EXPORT_PRIVATE HostResolverInternalAliasResult final
+    : public HostResolverInternalResult {
+ public:
+  static std::unique_ptr<HostResolverInternalAliasResult> FromValue(
+      const base::Value& value);
+
+  // `domain_name` and `alias_target` are dotted form domain names.
+  HostResolverInternalAliasResult(std::string domain_name,
+                                  DnsQueryType query_type,
+                                  absl::optional<base::TimeTicks> expiration,
+                                  base::Time timed_expiration,
+                                  Source source,
+                                  std::string alias_target);
+  ~HostResolverInternalAliasResult() override = default;
+
+  HostResolverInternalAliasResult(const HostResolverInternalAliasResult&) =
+      delete;
+  HostResolverInternalAliasResult& operator=(
+      const HostResolverInternalAliasResult&) = delete;
+
+  bool operator==(const HostResolverInternalAliasResult& other) const {
+    return HostResolverInternalResult::operator==(other) &&
+           alias_target_ == other.alias_target_;
+  }
+
+  const std::string& alias_target() const { return alias_target_; }
+
+  base::Value ToValue() const override;
+
+ private:
+  HostResolverInternalAliasResult(const base::Value::Dict& dict,
+                                  std::string alias_target);
+
+  const std::string alias_target_;
+};
+
+}  // namespace net
+
+#endif  // NET_DNS_HOST_RESOLVER_INTERNAL_RESULT_H_
diff --git a/net/dns/host_resolver_internal_result_unittest.cc b/net/dns/host_resolver_internal_result_unittest.cc
new file mode 100644
index 0000000..a7271d0a
--- /dev/null
+++ b/net/dns/host_resolver_internal_result_unittest.cc
@@ -0,0 +1,614 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/host_resolver_internal_result.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/time/time.h"
+#include "net/base/connection_endpoint_metadata.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/dns/https_record_rdata.h"
+#include "net/dns/public/dns_query_type.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using ::testing::ElementsAre;
+using ::testing::Optional;
+using ::testing::Ref;
+
+namespace net {
+namespace {
+
+TEST(HostResolverInternalResultTest, DeserializeMalformedValue) {
+  base::Value non_dict(base::Value::Type::BOOLEAN);
+  EXPECT_FALSE(HostResolverInternalResult::FromValue(non_dict));
+
+  base::Value missing_type(base::Value::Type::DICTIONARY);
+  EXPECT_FALSE(HostResolverInternalResult::FromValue(missing_type));
+
+  base::Value bad_type(base::Value::Type::DICTIONARY);
+  bad_type.GetDict().Set("type", "foo");
+  EXPECT_FALSE(HostResolverInternalResult::FromValue(bad_type));
+}
+
+TEST(HostResolverInternalResultTest, DataResult) {
+  auto result = std::make_unique<HostResolverInternalDataResult>(
+      "domain.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::vector<IPEndPoint>{IPEndPoint(IPAddress(2, 2, 2, 2), 46)},
+      std::vector<std::string>{"foo", "bar"},
+      std::vector<HostPortPair>{HostPortPair("anotherdomain.test", 112)});
+
+  EXPECT_EQ(result->domain_name(), "domain.test");
+  EXPECT_EQ(result->query_type(), DnsQueryType::AAAA);
+  EXPECT_EQ(result->type(), HostResolverInternalResult::Type::kData);
+  EXPECT_EQ(result->source(), HostResolverInternalResult::Source::kDns);
+  EXPECT_THAT(result->expiration(), Optional(base::TimeTicks()));
+  EXPECT_THAT(result->timed_expiration(), Optional(base::Time()));
+
+  EXPECT_THAT(result->AsData(), Ref(*result));
+
+  EXPECT_THAT(result->endpoints(),
+              ElementsAre(IPEndPoint(IPAddress(2, 2, 2, 2), 46)));
+  EXPECT_THAT(result->strings(), ElementsAre("foo", "bar"));
+  EXPECT_THAT(result->hosts(),
+              ElementsAre(HostPortPair("anotherdomain.test", 112)));
+}
+
+TEST(HostResolverInternalResultTest, RoundtripDataResultThroughSerialization) {
+  auto result = std::make_unique<HostResolverInternalDataResult>(
+      "domain.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::vector<IPEndPoint>{IPEndPoint(IPAddress(2, 2, 2, 2), 46)},
+      std::vector<std::string>{"foo", "bar"},
+      std::vector<HostPortPair>{HostPortPair("anotherdomain.test", 112)});
+
+  base::Value value = result->ToValue();
+  auto deserialized = HostResolverInternalResult::FromValue(value);
+  ASSERT_TRUE(deserialized);
+  ASSERT_EQ(deserialized->type(), HostResolverInternalResult::Type::kData);
+
+  // Expect deserialized result to be the same as the original other than
+  // missing non-timed expiration.
+  EXPECT_EQ(deserialized->AsData(),
+            HostResolverInternalDataResult(
+                result->domain_name(), result->query_type(),
+                /*expiration=*/absl::nullopt,
+                result->timed_expiration().value(), result->source(),
+                result->endpoints(), result->strings(), result->hosts()));
+}
+
+// Expect results to serialize to a consistent base::Value format for
+// consumption by NetLog and similar.
+TEST(HostResolverInternalResultTest, SerializepDataResult) {
+  auto result = std::make_unique<HostResolverInternalDataResult>(
+      "domain.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::vector<IPEndPoint>{IPEndPoint(IPAddress(2, 2, 2, 2), 46)},
+      std::vector<std::string>{"foo", "bar"},
+      std::vector<HostPortPair>{HostPortPair("anotherdomain.test", 112)});
+  base::Value value = result->ToValue();
+
+  absl::optional<base::Value> expected = base::JSONReader::Read(
+      R"(
+        {
+          "domain_name": "domain.test",
+          "endpoints": [
+            {
+              "address": "2.2.2.2",
+              "port": 46
+            }
+          ],
+          "hosts": [
+            {
+              "host": "anotherdomain.test",
+              "port": 112
+            }
+          ],
+          "query_type": "AAAA",
+          "source": "dns",
+          "strings": [
+            "foo",
+            "bar"
+          ],
+          "timed_expiration": "0",
+          "type": "data"
+        }
+        )");
+  ASSERT_TRUE(expected.has_value());
+
+  EXPECT_EQ(value, expected.value());
+}
+
+TEST(HostResolverInternalResultTest, DeserializeMalformedDataValue) {
+  auto result = std::make_unique<HostResolverInternalDataResult>(
+      "domain.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::vector<IPEndPoint>{IPEndPoint(IPAddress(2, 2, 2, 2), 46)},
+      std::vector<std::string>{"foo", "bar"},
+      std::vector<HostPortPair>{HostPortPair("anotherdomain.test", 112)});
+  base::Value valid_value = result->ToValue();
+  ASSERT_TRUE(HostResolverInternalDataResult::FromValue(valid_value));
+
+  base::Value missing_domain = valid_value.Clone();
+  ASSERT_TRUE(missing_domain.GetDict().Remove("domain_name"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_domain));
+
+  base::Value missing_qtype = valid_value.Clone();
+  ASSERT_TRUE(missing_qtype.GetDict().Remove("query_type"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_qtype));
+  base::Value unknown_qtype = valid_value.Clone();
+  ASSERT_TRUE(unknown_qtype.GetDict().Set("query_type", "foo"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(unknown_qtype));
+
+  base::Value missing_value_type = valid_value.Clone();
+  ASSERT_TRUE(missing_value_type.GetDict().Remove("type"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_value_type));
+  base::Value unknown_value_type = valid_value.Clone();
+  ASSERT_TRUE(unknown_value_type.GetDict().Set("type", "foo"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(unknown_value_type));
+
+  base::Value missing_source = valid_value.Clone();
+  ASSERT_TRUE(missing_source.GetDict().Remove("source"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_source));
+  base::Value unknown_source = valid_value.Clone();
+  ASSERT_TRUE(unknown_source.GetDict().Set("source", "foo"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(unknown_source));
+
+  base::Value missing_expiration = valid_value.Clone();
+  ASSERT_TRUE(missing_expiration.GetDict().Remove("timed_expiration"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_expiration));
+  base::Value invalid_expiration = valid_value.Clone();
+  ASSERT_TRUE(invalid_expiration.GetDict().Set("timed_expiration", "foo"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(invalid_expiration));
+
+  base::Value missing_endpoints = valid_value.Clone();
+  ASSERT_TRUE(missing_endpoints.GetDict().Remove("endpoints"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_endpoints));
+  base::Value invalid_endpoint = valid_value.Clone();
+  invalid_endpoint.GetDict().FindList("endpoints")->front() =
+      base::Value("foo");
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(invalid_endpoint));
+
+  base::Value missing_strings = valid_value.Clone();
+  ASSERT_TRUE(missing_strings.GetDict().Remove("strings"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_strings));
+  base::Value invalid_string = valid_value.Clone();
+  invalid_string.GetDict().FindList("strings")->front() = base::Value(5);
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(invalid_string));
+
+  base::Value missing_hosts = valid_value.Clone();
+  ASSERT_TRUE(missing_hosts.GetDict().Remove("hosts"));
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(missing_hosts));
+  base::Value invalid_hosts = valid_value.Clone();
+  invalid_hosts.GetDict().FindList("hosts")->front() = base::Value("foo");
+  EXPECT_FALSE(HostResolverInternalDataResult::FromValue(invalid_hosts));
+}
+
+TEST(HostResolverInternalResultTest, MetadataResult) {
+  const ConnectionEndpointMetadata kMetadata(
+      /*supported_protocol_alpns=*/{"http/1.1", "h3"},
+      /*ech_config_list=*/{0x01, 0x13},
+      /*target_name*/ "target.test");
+  auto result = std::make_unique<HostResolverInternalMetadataResult>(
+      "domain1.test", DnsQueryType::HTTPS, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {4, kMetadata}});
+
+  EXPECT_EQ(result->domain_name(), "domain1.test");
+  EXPECT_EQ(result->query_type(), DnsQueryType::HTTPS);
+  EXPECT_EQ(result->type(), HostResolverInternalResult::Type::kMetadata);
+  EXPECT_EQ(result->source(), HostResolverInternalResult::Source::kDns);
+  EXPECT_THAT(result->expiration(), Optional(base::TimeTicks()));
+  EXPECT_THAT(result->timed_expiration(), Optional(base::Time()));
+
+  EXPECT_THAT(result->AsMetadata(), Ref(*result));
+
+  EXPECT_THAT(result->metadatas(), ElementsAre(std::make_pair(4, kMetadata)));
+}
+
+TEST(HostResolverInternalResultTest,
+     RoundtripMetadataResultThroughSerialization) {
+  const ConnectionEndpointMetadata kMetadata(
+      /*supported_protocol_alpns=*/{"http/1.1", "h2", "h3"},
+      /*ech_config_list=*/{0x01, 0x13, 0x15},
+      /*target_name*/ "target1.test");
+  auto result = std::make_unique<HostResolverInternalMetadataResult>(
+      "domain2.test", DnsQueryType::HTTPS, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {2, kMetadata}});
+
+  base::Value value = result->ToValue();
+  auto deserialized = HostResolverInternalResult::FromValue(value);
+  ASSERT_TRUE(deserialized);
+  ASSERT_EQ(deserialized->type(), HostResolverInternalResult::Type::kMetadata);
+
+  // Expect deserialized result to be the same as the original other than
+  // missing non-timed expiration.
+  EXPECT_EQ(
+      deserialized->AsMetadata(),
+      HostResolverInternalMetadataResult(
+          result->domain_name(), result->query_type(),
+          /*expiration=*/absl::nullopt, result->timed_expiration().value(),
+          result->source(), result->metadatas()));
+}
+
+// Expect results to serialize to a consistent base::Value format for
+// consumption by NetLog and similar.
+TEST(HostResolverInternalResultTest, SerializepMetadataResult) {
+  const ConnectionEndpointMetadata kMetadata(
+      /*supported_protocol_alpns=*/{"http/1.1", "h2", "h3"},
+      /*ech_config_list=*/{0x01, 0x13, 0x15},
+      /*target_name*/ "target1.test");
+  auto result = std::make_unique<HostResolverInternalMetadataResult>(
+      "domain2.test", DnsQueryType::HTTPS, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {2, kMetadata}});
+  base::Value value = result->ToValue();
+
+  // Note that the `ech_config_list` base64 encodes to "ARMV".
+  absl::optional<base::Value> expected = base::JSONReader::Read(
+      R"(
+        {
+          "domain_name": "domain2.test",
+          "metadatas": [
+            {
+              "metadata_value":
+              {
+                "ech_config_list": "ARMV",
+                "supported_protocol_alpns": ["http/1.1", "h2", "h3"],
+                "target_name": "target1.test"
+              },
+              "metadata_weight": 2
+            }
+          ],
+          "query_type": "HTTPS",
+          "source": "dns",
+          "timed_expiration": "0",
+          "type": "metadata"
+        }
+        )");
+  ASSERT_TRUE(expected.has_value());
+
+  EXPECT_EQ(value, expected.value());
+}
+
+TEST(HostResolverInternalResultTest, DeserializeMalformedMetadataValue) {
+  const ConnectionEndpointMetadata kMetadata(
+      /*supported_protocol_alpns=*/{"http/1.1", "h2", "h3"},
+      /*ech_config_list=*/{0x01, 0x13, 0x15},
+      /*target_name*/ "target1.test");
+  auto result = std::make_unique<HostResolverInternalMetadataResult>(
+      "domain2.test", DnsQueryType::HTTPS, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {2, kMetadata}});
+  base::Value valid_value = result->ToValue();
+  ASSERT_TRUE(HostResolverInternalMetadataResult::FromValue(valid_value));
+
+  base::Value missing_domain = valid_value.Clone();
+  ASSERT_TRUE(missing_domain.GetDict().Remove("domain_name"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(missing_domain));
+
+  base::Value missing_qtype = valid_value.Clone();
+  ASSERT_TRUE(missing_qtype.GetDict().Remove("query_type"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(missing_qtype));
+  base::Value unknown_qtype = valid_value.Clone();
+  ASSERT_TRUE(unknown_qtype.GetDict().Set("query_type", "foo"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(unknown_qtype));
+
+  base::Value missing_value_type = valid_value.Clone();
+  ASSERT_TRUE(missing_value_type.GetDict().Remove("type"));
+  EXPECT_FALSE(
+      HostResolverInternalMetadataResult::FromValue(missing_value_type));
+  base::Value unknown_value_type = valid_value.Clone();
+  ASSERT_TRUE(unknown_value_type.GetDict().Set("type", "foo"));
+  EXPECT_FALSE(
+      HostResolverInternalMetadataResult::FromValue(unknown_value_type));
+
+  base::Value missing_source = valid_value.Clone();
+  ASSERT_TRUE(missing_source.GetDict().Remove("source"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(missing_source));
+  base::Value unknown_source = valid_value.Clone();
+  ASSERT_TRUE(unknown_source.GetDict().Set("source", "foo"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(unknown_source));
+
+  base::Value missing_expiration = valid_value.Clone();
+  ASSERT_TRUE(missing_expiration.GetDict().Remove("timed_expiration"));
+  EXPECT_FALSE(
+      HostResolverInternalMetadataResult::FromValue(missing_expiration));
+  base::Value invalid_expiration = valid_value.Clone();
+  ASSERT_TRUE(invalid_expiration.GetDict().Set("timed_expiration", "foo"));
+  EXPECT_FALSE(
+      HostResolverInternalMetadataResult::FromValue(invalid_expiration));
+
+  base::Value missing_metadatas = valid_value.Clone();
+  ASSERT_TRUE(missing_metadatas.GetDict().Remove("metadatas"));
+  EXPECT_FALSE(
+      HostResolverInternalMetadataResult::FromValue(missing_metadatas));
+  base::Value invalid_metadatas = valid_value.Clone();
+  *invalid_metadatas.GetDict().Find("metadatas") = base::Value(4);
+  EXPECT_FALSE(
+      HostResolverInternalMetadataResult::FromValue(invalid_metadatas));
+
+  base::Value missing_weight = valid_value.Clone();
+  ASSERT_TRUE(missing_weight.GetDict()
+                  .Find("metadatas")
+                  ->GetList()
+                  .front()
+                  .GetDict()
+                  .Remove("metadata_weight"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(missing_weight));
+  base::Value invalid_weight = valid_value.Clone();
+  *invalid_weight.GetDict()
+       .Find("metadatas")
+       ->GetList()
+       .front()
+       .GetDict()
+       .Find("metadata_weight") = base::Value("foo");
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(invalid_weight));
+
+  base::Value missing_value = valid_value.Clone();
+  ASSERT_TRUE(missing_value.GetDict()
+                  .Find("metadatas")
+                  ->GetList()
+                  .front()
+                  .GetDict()
+                  .Remove("metadata_value"));
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(missing_value));
+  base::Value invalid_value = valid_value.Clone();
+  *invalid_value.GetDict()
+       .Find("metadatas")
+       ->GetList()
+       .front()
+       .GetDict()
+       .Find("metadata_value") = base::Value("foo");
+  EXPECT_FALSE(HostResolverInternalMetadataResult::FromValue(invalid_value));
+}
+
+TEST(HostResolverInternalResultTest, ErrorResult) {
+  auto result = std::make_unique<HostResolverInternalErrorResult>(
+      "domain3.test", DnsQueryType::PTR, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kUnknown, ERR_NAME_NOT_RESOLVED);
+
+  EXPECT_EQ(result->domain_name(), "domain3.test");
+  EXPECT_EQ(result->query_type(), DnsQueryType::PTR);
+  EXPECT_EQ(result->type(), HostResolverInternalResult::Type::kError);
+  EXPECT_EQ(result->source(), HostResolverInternalResult::Source::kUnknown);
+  EXPECT_THAT(result->expiration(), Optional(base::TimeTicks()));
+  EXPECT_THAT(result->timed_expiration(), Optional(base::Time()));
+
+  EXPECT_THAT(result->AsError(), Ref(*result));
+
+  EXPECT_EQ(result->error(), ERR_NAME_NOT_RESOLVED);
+}
+
+TEST(HostResolverInternalResultTest, NoncachableErrorResult) {
+  auto result = std::make_unique<HostResolverInternalErrorResult>(
+      "domain3.test", DnsQueryType::PTR, /*expiration=*/absl::nullopt,
+      /*timed_expiration=*/absl::nullopt,
+      HostResolverInternalResult::Source::kUnknown, ERR_NAME_NOT_RESOLVED);
+
+  EXPECT_EQ(result->domain_name(), "domain3.test");
+  EXPECT_EQ(result->query_type(), DnsQueryType::PTR);
+  EXPECT_EQ(result->type(), HostResolverInternalResult::Type::kError);
+  EXPECT_EQ(result->source(), HostResolverInternalResult::Source::kUnknown);
+  EXPECT_FALSE(result->expiration().has_value());
+  EXPECT_FALSE(result->timed_expiration().has_value());
+
+  EXPECT_THAT(result->AsError(), Ref(*result));
+
+  EXPECT_EQ(result->error(), ERR_NAME_NOT_RESOLVED);
+}
+
+TEST(HostResolverInternalResultTest, RoundtripErrorResultThroughSerialization) {
+  auto result = std::make_unique<HostResolverInternalErrorResult>(
+      "domain4.test", DnsQueryType::A, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, ERR_DNS_SERVER_FAILED);
+
+  base::Value value = result->ToValue();
+  auto deserialized = HostResolverInternalResult::FromValue(value);
+  ASSERT_TRUE(deserialized);
+  ASSERT_EQ(deserialized->type(), HostResolverInternalResult::Type::kError);
+
+  // Expect deserialized result to be the same as the original other than
+  // missing non-timed expiration.
+  EXPECT_EQ(deserialized->AsError(),
+            HostResolverInternalErrorResult(result->domain_name(),
+                                            result->query_type(),
+                                            /*expiration=*/absl::nullopt,
+                                            result->timed_expiration().value(),
+                                            result->source(), result->error()));
+}
+
+// Expect results to serialize to a consistent base::Value format for
+// consumption by NetLog and similar.
+TEST(HostResolverInternalResultTest, SerializepErrorResult) {
+  auto result = std::make_unique<HostResolverInternalErrorResult>(
+      "domain4.test", DnsQueryType::A, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, ERR_DNS_SERVER_FAILED);
+  base::Value value = result->ToValue();
+
+  absl::optional<base::Value> expected = base::JSONReader::Read(
+      R"(
+        {
+          "domain_name": "domain4.test",
+          "error": -802,
+          "query_type": "A",
+          "source": "dns",
+          "timed_expiration": "0",
+          "type": "error"
+        }
+        )");
+  ASSERT_TRUE(expected.has_value());
+
+  EXPECT_EQ(value, expected.value());
+}
+
+TEST(HostResolverInternalResultTest, DeserializeMalformedErrorValue) {
+  auto result = std::make_unique<HostResolverInternalErrorResult>(
+      "domain4.test", DnsQueryType::A, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, ERR_DNS_SERVER_FAILED);
+  base::Value valid_value = result->ToValue();
+  ASSERT_TRUE(HostResolverInternalErrorResult::FromValue(valid_value));
+
+  base::Value missing_domain = valid_value.Clone();
+  ASSERT_TRUE(missing_domain.GetDict().Remove("domain_name"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(missing_domain));
+
+  base::Value missing_qtype = valid_value.Clone();
+  ASSERT_TRUE(missing_qtype.GetDict().Remove("query_type"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(missing_qtype));
+  base::Value unknown_qtype = valid_value.Clone();
+  ASSERT_TRUE(unknown_qtype.GetDict().Set("query_type", "foo"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(unknown_qtype));
+
+  base::Value missing_value_type = valid_value.Clone();
+  ASSERT_TRUE(missing_value_type.GetDict().Remove("type"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(missing_value_type));
+  base::Value unknown_value_type = valid_value.Clone();
+  ASSERT_TRUE(unknown_value_type.GetDict().Set("type", "foo"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(unknown_value_type));
+
+  base::Value missing_source = valid_value.Clone();
+  ASSERT_TRUE(missing_source.GetDict().Remove("source"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(missing_source));
+  base::Value unknown_source = valid_value.Clone();
+  ASSERT_TRUE(unknown_source.GetDict().Set("source", "foo"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(unknown_source));
+
+  base::Value invalid_expiration = valid_value.Clone();
+  ASSERT_TRUE(invalid_expiration.GetDict().Set("timed_expiration", "foo"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(invalid_expiration));
+
+  base::Value missing_error = valid_value.Clone();
+  ASSERT_TRUE(missing_error.GetDict().Remove("error"));
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(missing_error));
+  base::Value invalid_error = valid_value.Clone();
+  *invalid_error.GetDict().Find("error") = base::Value("foo");
+  EXPECT_FALSE(HostResolverInternalErrorResult::FromValue(invalid_error));
+}
+
+TEST(HostResolverInternalResultTest, AliasResult) {
+  auto result = std::make_unique<HostResolverInternalAliasResult>(
+      "domain5.test", DnsQueryType::HTTPS, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, "alias_target.test");
+
+  EXPECT_EQ(result->domain_name(), "domain5.test");
+  EXPECT_EQ(result->query_type(), DnsQueryType::HTTPS);
+  EXPECT_EQ(result->type(), HostResolverInternalResult::Type::kAlias);
+  EXPECT_EQ(result->source(), HostResolverInternalResult::Source::kDns);
+  EXPECT_THAT(result->expiration(), Optional(base::TimeTicks()));
+  EXPECT_THAT(result->timed_expiration(), Optional(base::Time()));
+
+  EXPECT_THAT(result->AsAlias(), Ref(*result));
+
+  EXPECT_THAT(result->alias_target(), "alias_target.test");
+}
+
+TEST(HostResolverInternalResultTest, RoundtripAliasResultThroughSerialization) {
+  auto result = std::make_unique<HostResolverInternalAliasResult>(
+      "domain6.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, "alias_target1.test");
+
+  base::Value value = result->ToValue();
+  auto deserialized = HostResolverInternalResult::FromValue(value);
+  ASSERT_TRUE(deserialized);
+  ASSERT_EQ(deserialized->type(), HostResolverInternalResult::Type::kAlias);
+
+  // Expect deserialized result to be the same as the original other than
+  // missing non-timed expiration.
+  EXPECT_EQ(
+      deserialized->AsAlias(),
+      HostResolverInternalAliasResult(
+          result->domain_name(), result->query_type(),
+          /*expiration=*/absl::nullopt, result->timed_expiration().value(),
+          result->source(), result->alias_target()));
+}
+
+// Expect results to serialize to a consistent base::Value format for
+// consumption by NetLog and similar.
+TEST(HostResolverInternalResultTest, SerializepAliasResult) {
+  auto result = std::make_unique<HostResolverInternalAliasResult>(
+      "domain6.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, "alias_target1.test");
+  base::Value value = result->ToValue();
+
+  absl::optional<base::Value> expected = base::JSONReader::Read(
+      R"(
+        {
+          "alias_target": "alias_target1.test",
+          "domain_name": "domain6.test",
+          "query_type": "AAAA",
+          "source": "dns",
+          "timed_expiration": "0",
+          "type": "alias"
+        }
+        )");
+  ASSERT_TRUE(expected.has_value());
+
+  EXPECT_EQ(value, expected.value());
+}
+
+TEST(HostResolverInternalResultTest, DeserializeMalformedAliasValue) {
+  auto result = std::make_unique<HostResolverInternalAliasResult>(
+      "domain6.test", DnsQueryType::AAAA, base::TimeTicks(), base::Time(),
+      HostResolverInternalResult::Source::kDns, "alias_target1.test");
+  base::Value valid_value = result->ToValue();
+  ASSERT_TRUE(HostResolverInternalAliasResult::FromValue(valid_value));
+
+  base::Value missing_domain = valid_value.Clone();
+  ASSERT_TRUE(missing_domain.GetDict().Remove("domain_name"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(missing_domain));
+
+  base::Value missing_qtype = valid_value.Clone();
+  ASSERT_TRUE(missing_qtype.GetDict().Remove("query_type"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(missing_qtype));
+  base::Value unknown_qtype = valid_value.Clone();
+  ASSERT_TRUE(unknown_qtype.GetDict().Set("query_type", "foo"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(unknown_qtype));
+
+  base::Value missing_value_type = valid_value.Clone();
+  ASSERT_TRUE(missing_value_type.GetDict().Remove("type"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(missing_value_type));
+  base::Value unknown_value_type = valid_value.Clone();
+  ASSERT_TRUE(unknown_value_type.GetDict().Set("type", "foo"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(unknown_value_type));
+
+  base::Value missing_source = valid_value.Clone();
+  ASSERT_TRUE(missing_source.GetDict().Remove("source"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(missing_source));
+  base::Value unknown_source = valid_value.Clone();
+  ASSERT_TRUE(unknown_source.GetDict().Set("source", "foo"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(unknown_source));
+
+  base::Value missing_expiration = valid_value.Clone();
+  ASSERT_TRUE(missing_expiration.GetDict().Remove("timed_expiration"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(missing_expiration));
+  base::Value invalid_expiration = valid_value.Clone();
+  ASSERT_TRUE(invalid_expiration.GetDict().Set("timed_expiration", "foo"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(invalid_expiration));
+
+  base::Value missing_alias = valid_value.Clone();
+  ASSERT_TRUE(missing_alias.GetDict().Remove("alias_target"));
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(missing_alias));
+  base::Value invalid_alias = valid_value.Clone();
+  *invalid_alias.GetDict().Find("alias_target") = base::Value(5);
+  EXPECT_FALSE(HostResolverInternalAliasResult::FromValue(invalid_alias));
+}
+
+}  // namespace
+}  // namespace net
diff --git a/net/test/test_connection_cost_observer.cc b/net/test/test_connection_cost_observer.cc
deleted file mode 100644
index 7967e63e..0000000
--- a/net/test/test_connection_cost_observer.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/test/test_connection_cost_observer.h"
-
-namespace net {
-
-TestConnectionCostObserver::TestConnectionCostObserver() = default;
-
-TestConnectionCostObserver::~TestConnectionCostObserver() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-void TestConnectionCostObserver::OnConnectionCostChanged(
-    NetworkChangeNotifier::ConnectionCost cost) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  cost_changed_inputs_.push_back(cost);
-
-  if (run_loop_) {
-    run_loop_->Quit();
-  }
-}
-
-void TestConnectionCostObserver::WaitForConnectionCostChanged() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  run_loop_ = std::make_unique<base::RunLoop>();
-  run_loop_->Run();
-  run_loop_.reset();
-}
-
-size_t TestConnectionCostObserver::cost_changed_calls() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return cost_changed_inputs_.size();
-}
-
-std::vector<NetworkChangeNotifier::ConnectionCost>
-TestConnectionCostObserver::cost_changed_inputs() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return cost_changed_inputs_;
-}
-
-NetworkChangeNotifier::ConnectionCost
-TestConnectionCostObserver::last_cost_changed_input() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK_GT(cost_changed_inputs_.size(), 0u);
-  return cost_changed_inputs_.back();
-}
-
-}  // namespace net
diff --git a/net/test/test_connection_cost_observer.h b/net/test/test_connection_cost_observer.h
deleted file mode 100644
index 913368a..0000000
--- a/net/test/test_connection_cost_observer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_TEST_TEST_CONNECTION_COST_OBSERVER_H_
-#define NET_TEST_TEST_CONNECTION_COST_OBSERVER_H_
-
-#include "base/run_loop.h"
-#include "base/sequence_checker.h"
-#include "net/base/network_change_notifier.h"
-
-namespace net {
-
-class TestConnectionCostObserver final
-    : public NetworkChangeNotifier::ConnectionCostObserver {
- public:
-  TestConnectionCostObserver();
-  ~TestConnectionCostObserver() final;
-
-  void OnConnectionCostChanged(
-      NetworkChangeNotifier::ConnectionCost cost) final;
-
-  void WaitForConnectionCostChanged();
-
-  size_t cost_changed_calls() const;
-  std::vector<NetworkChangeNotifier::ConnectionCost> cost_changed_inputs()
-      const;
-  NetworkChangeNotifier::ConnectionCost last_cost_changed_input() const;
-
-  TestConnectionCostObserver(const TestConnectionCostObserver&) = delete;
-  TestConnectionCostObserver& operator=(const TestConnectionCostObserver&) =
-      delete;
-
- private:
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  // Set and used to block in `WaitForConnectionCostChanged()` until the next
-  // cost changed event occurs.
-  std::unique_ptr<base::RunLoop> run_loop_;
-
-  // Record each `OnConnectionCostChanged()` call.
-  std::vector<NetworkChangeNotifier::ConnectionCost> cost_changed_inputs_;
-};
-
-}  // namespace net
-
-#endif  // NET_TEST_TEST_CONNECTION_COST_OBSERVER_H_
diff --git a/net/test/win/fake_network_cost_manager.cc b/net/test/win/fake_network_cost_manager.cc
deleted file mode 100644
index 7c053ff..0000000
--- a/net/test/win/fake_network_cost_manager.cc
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/test/win/fake_network_cost_manager.h"
-
-#include <netlistmgr.h>
-#include <wrl/implements.h>
-
-#include <map>
-
-#include "net/base/network_cost_change_notifier_win.h"
-
-using Microsoft::WRL::ClassicCom;
-using Microsoft::WRL::ComPtr;
-using Microsoft::WRL::RuntimeClass;
-using Microsoft::WRL::RuntimeClassFlags;
-
-namespace net {
-
-namespace {
-
-DWORD NlmConnectionCostFlagsFromConnectionCost(
-    NetworkChangeNotifier::ConnectionCost source_cost) {
-  switch (source_cost) {
-    case NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED:
-      return (NLM_CONNECTION_COST_UNRESTRICTED | NLM_CONNECTION_COST_CONGESTED);
-    case NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED:
-      return (NLM_CONNECTION_COST_VARIABLE | NLM_CONNECTION_COST_ROAMING |
-              NLM_CONNECTION_COST_APPROACHINGDATALIMIT);
-    case NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN:
-    default:
-      return NLM_CONNECTION_COST_UNKNOWN;
-  }
-}
-
-void DispatchCostChangedEvent(ComPtr<INetworkCostManagerEvents> event_target,
-                              DWORD cost) {
-  std::ignore =
-      event_target->CostChanged(cost, /*destination_address=*/nullptr);
-}
-
-}  // namespace
-
-// A fake implementation of INetworkCostManager that can simulate costs, changed
-// costs and errors.
-class FakeNetworkCostManager final
-    : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
-                          INetworkCostManager,
-                          IConnectionPointContainer,
-                          IConnectionPoint> {
- public:
-  FakeNetworkCostManager(NetworkChangeNotifier::ConnectionCost connection_cost,
-                         NetworkCostManagerStatus error_status)
-      : error_status_(error_status), connection_cost_(connection_cost) {}
-
-  // For each event sink in `event_sinks_`, call
-  // `INetworkCostManagerEvents::CostChanged` with `changed_cost` on the event
-  // sink's task runner.
-  void PostCostChangedEvents(
-      NetworkChangeNotifier::ConnectionCost changed_cost) {
-    DWORD cost_for_changed_event;
-    std::map</*event_sink_cookie=*/DWORD, EventSinkRegistration>
-        event_sinks_for_changed_event;
-    {
-      base::AutoLock auto_lock(member_lock_);
-      connection_cost_ = changed_cost;
-      cost_for_changed_event =
-          NlmConnectionCostFlagsFromConnectionCost(changed_cost);
-
-      // Get the snapshot of event sinks to notify.  The snapshot collection
-      // creates a new ComPtr for each event sink, which increments each the
-      // event sink's reference count, ensuring that each event sink
-      // remains alive to receive the cost changed event notification.
-      event_sinks_for_changed_event = event_sinks_;
-    }
-
-    for (const auto& pair : event_sinks_for_changed_event) {
-      const auto& registration = pair.second;
-      registration.event_sink_task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(&DispatchCostChangedEvent, registration.event_sink_,
-                         cost_for_changed_event));
-    }
-  }
-
-  // Implement the INetworkCostManager interface.
-  HRESULT
-  __stdcall GetCost(DWORD* cost,
-                    NLM_SOCKADDR* destination_ip_address) override {
-    if (error_status_ == NetworkCostManagerStatus::kErrorGetCostFailed) {
-      return E_FAIL;
-    }
-
-    if (destination_ip_address != nullptr) {
-      NOTIMPLEMENTED();
-      return E_NOTIMPL;
-    }
-
-    {
-      base::AutoLock auto_lock(member_lock_);
-      *cost = NlmConnectionCostFlagsFromConnectionCost(connection_cost_);
-    }
-    return S_OK;
-  }
-
-  HRESULT __stdcall GetDataPlanStatus(
-      NLM_DATAPLAN_STATUS* data_plan_status,
-      NLM_SOCKADDR* destination_ip_address) override {
-    NOTIMPLEMENTED();
-    return E_NOTIMPL;
-  }
-
-  HRESULT __stdcall SetDestinationAddresses(
-      UINT32 length,
-      NLM_SOCKADDR* destination_ip_address_list,
-      VARIANT_BOOL append) override {
-    NOTIMPLEMENTED();
-    return E_NOTIMPL;
-  }
-
-  // Implement the IConnectionPointContainer interface.
-  HRESULT __stdcall FindConnectionPoint(REFIID connection_point_id,
-                                        IConnectionPoint** result) override {
-    if (error_status_ ==
-        NetworkCostManagerStatus::kErrorFindConnectionPointFailed) {
-      return E_ABORT;
-    }
-
-    if (connection_point_id != IID_INetworkCostManagerEvents) {
-      return E_NOINTERFACE;
-    }
-
-    *result = static_cast<IConnectionPoint*>(this);
-    AddRef();
-    return S_OK;
-  }
-
-  HRESULT __stdcall EnumConnectionPoints(
-      IEnumConnectionPoints** results) override {
-    NOTIMPLEMENTED();
-    return E_NOTIMPL;
-  }
-
-  // Implement the IConnectionPoint interface.
-  HRESULT __stdcall Advise(IUnknown* event_sink,
-                           DWORD* event_sink_cookie) override {
-    if (error_status_ == NetworkCostManagerStatus::kErrorAdviseFailed) {
-      return E_NOT_VALID_STATE;
-    }
-
-    ComPtr<INetworkCostManagerEvents> cost_manager_event_sink;
-    HRESULT hr =
-        event_sink->QueryInterface(IID_PPV_ARGS(&cost_manager_event_sink));
-    if (hr != S_OK) {
-      return hr;
-    }
-
-    base::AutoLock auto_lock(member_lock_);
-
-    event_sinks_[next_event_sink_cookie_] = {
-        cost_manager_event_sink, base::SequencedTaskRunnerHandle::Get()};
-
-    *event_sink_cookie = next_event_sink_cookie_;
-    ++next_event_sink_cookie_;
-
-    return S_OK;
-  }
-
-  HRESULT __stdcall Unadvise(DWORD event_sink_cookie) override {
-    base::AutoLock auto_lock(member_lock_);
-
-    auto it = event_sinks_.find(event_sink_cookie);
-    if (it == event_sinks_.end()) {
-      return ERROR_NOT_FOUND;
-    }
-
-    event_sinks_.erase(it);
-    return S_OK;
-  }
-
-  HRESULT __stdcall GetConnectionInterface(IID* result) override {
-    NOTIMPLEMENTED();
-    return E_NOTIMPL;
-  }
-
-  HRESULT __stdcall GetConnectionPointContainer(
-      IConnectionPointContainer** result) override {
-    NOTIMPLEMENTED();
-    return E_NOTIMPL;
-  }
-
-  HRESULT __stdcall EnumConnections(IEnumConnections** result) override {
-    NOTIMPLEMENTED();
-    return E_NOTIMPL;
-  }
-
-  // Implement the IUnknown interface.
-  HRESULT __stdcall QueryInterface(REFIID interface_id,
-                                   void** result) override {
-    if (error_status_ == NetworkCostManagerStatus::kErrorQueryInterfaceFailed) {
-      return E_NOINTERFACE;
-    }
-    return RuntimeClass<RuntimeClassFlags<ClassicCom>, INetworkCostManager,
-                        IConnectionPointContainer,
-                        IConnectionPoint>::QueryInterface(interface_id, result);
-  }
-
-  FakeNetworkCostManager(const FakeNetworkCostManager&) = delete;
-  FakeNetworkCostManager& operator=(const FakeNetworkCostManager&) = delete;
-
- private:
-  // The error state for this FakeNetworkCostManager to simulate.  Cannot be
-  // changed.
-  const NetworkCostManagerStatus error_status_;
-
-  // Synchronizes access to all members below.
-  base::Lock member_lock_;
-
-  NetworkChangeNotifier::ConnectionCost connection_cost_
-      GUARDED_BY(member_lock_);
-
-  DWORD next_event_sink_cookie_ GUARDED_BY(member_lock_) = 0;
-
-  struct EventSinkRegistration {
-    ComPtr<INetworkCostManagerEvents> event_sink_;
-    scoped_refptr<base::SequencedTaskRunner> event_sink_task_runner_;
-  };
-  std::map</*event_sink_cookie=*/DWORD, EventSinkRegistration> event_sinks_
-      GUARDED_BY(member_lock_);
-};
-
-FakeNetworkCostManagerEnvironment::FakeNetworkCostManagerEnvironment() {
-  // Set up NetworkCostChangeNotifierWin to use the fake OS APIs.
-  NetworkCostChangeNotifierWin::OverrideCoCreateInstanceForTesting(
-      base::BindRepeating(
-          &FakeNetworkCostManagerEnvironment::FakeCoCreateInstance,
-          base::Unretained(this)));
-}
-
-FakeNetworkCostManagerEnvironment::~FakeNetworkCostManagerEnvironment() {
-  // Restore NetworkCostChangeNotifierWin to use the real OS APIs.
-  NetworkCostChangeNotifierWin::OverrideCoCreateInstanceForTesting(
-      NetworkCostChangeNotifierWin::CoCreateInstanceCallback());
-}
-
-HRESULT FakeNetworkCostManagerEnvironment::FakeCoCreateInstance(
-    REFCLSID class_id,
-    LPUNKNOWN outer_aggregate,
-    DWORD context_flags,
-    REFIID interface_id,
-    LPVOID* result) {
-  NetworkChangeNotifier::ConnectionCost connection_cost_for_new_instance;
-  NetworkCostManagerStatus error_status_for_new_instance;
-  {
-    base::AutoLock auto_lock(member_lock_);
-    connection_cost_for_new_instance = connection_cost_;
-    error_status_for_new_instance = error_status_;
-  }
-
-  if (error_status_for_new_instance ==
-      NetworkCostManagerStatus::kErrorCoCreateInstanceFailed) {
-    return E_ACCESSDENIED;
-  }
-
-  if (class_id != CLSID_NetworkListManager) {
-    return E_NOINTERFACE;
-  }
-
-  if (interface_id != IID_INetworkCostManager) {
-    return E_NOINTERFACE;
-  }
-
-  ComPtr<FakeNetworkCostManager> instance =
-      Microsoft::WRL::Make<FakeNetworkCostManager>(
-          connection_cost_for_new_instance, error_status_for_new_instance);
-  {
-    base::AutoLock auto_lock(member_lock_);
-    fake_network_cost_managers_.push_back(instance);
-  }
-  *result = instance.Detach();
-  return S_OK;
-}
-
-void FakeNetworkCostManagerEnvironment::SetCost(
-    NetworkChangeNotifier::ConnectionCost value) {
-  // Update the cost for each `INetworkCostManager` instance in
-  // `fake_network_cost_managers_`.
-  std::vector<Microsoft::WRL::ComPtr<FakeNetworkCostManager>>
-      fake_network_cost_managers_for_change_event;
-  {
-    base::AutoLock auto_lock(member_lock_);
-    connection_cost_ = value;
-    fake_network_cost_managers_for_change_event = fake_network_cost_managers_;
-  }
-
-  for (const auto& network_cost_manager :
-       fake_network_cost_managers_for_change_event) {
-    network_cost_manager->PostCostChangedEvents(/*connection_cost=*/value);
-  }
-}
-
-void FakeNetworkCostManagerEnvironment::SimulateError(
-    NetworkCostManagerStatus error_status) {
-  base::AutoLock auto_lock(member_lock_);
-  error_status_ = error_status;
-}
-
-}  // namespace net
diff --git a/net/test/win/fake_network_cost_manager.h b/net/test/win/fake_network_cost_manager.h
deleted file mode 100644
index fd6348e..0000000
--- a/net/test/win/fake_network_cost_manager.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_TEST_WIN_FAKE_NETWORK_COST_MANAGER_H_
-#define NET_TEST_WIN_FAKE_NETWORK_COST_MANAGER_H_
-
-#include <windows.h>
-#include <wrl/client.h>
-
-#include <vector>
-
-#include "base/synchronization/lock.h"
-#include "base/thread_annotations.h"
-#include "net/base/network_change_notifier.h"
-
-namespace net {
-class FakeNetworkCostManager;
-
-// Each value represents a different Windows OS API that can fail when
-// monitoring the cost of network connections.  Use with
-// `FakeNetworkCostManagerEnvironment::SimulateError` to simulate Windows OS API
-// failures that return error HRESULTS.
-enum class NetworkCostManagerStatus {
-  kOk,
-  kErrorCoCreateInstanceFailed,
-  kErrorQueryInterfaceFailed,
-  kErrorFindConnectionPointFailed,
-  kErrorAdviseFailed,
-  kErrorGetCostFailed,
-};
-
-// Provides a fake implementation of the INetworkCostManager Windows OS API for
-// NetworkCostChangeNotifierWin.  Must be constructed before any
-// NetworkCostChangeNotifierWin instances exist.  Sets up the fake OS API in the
-// constructor and restores the real OS API in the destructor.  Tests should use
-// this class to simulate different network costs, cost changed events and
-// errors without depending on the actual OS APIs or current network
-// environment.
-class FakeNetworkCostManagerEnvironment {
- public:
-  FakeNetworkCostManagerEnvironment();
-  ~FakeNetworkCostManagerEnvironment();
-
-  void SetCost(NetworkChangeNotifier::ConnectionCost value);
-  void SimulateError(NetworkCostManagerStatus error_status);
-
-  FakeNetworkCostManagerEnvironment(const FakeNetworkCostManagerEnvironment&) =
-      delete;
-  FakeNetworkCostManagerEnvironment& operator=(
-      const FakeNetworkCostManagerEnvironment&) = delete;
-
- private:
-  // Creates a fake implementation of INetworkCostManager.
-  HRESULT FakeCoCreateInstance(REFCLSID class_id,
-                               LPUNKNOWN outer_aggregate,
-                               DWORD context_flags,
-                               REFIID interface_id,
-                               LPVOID* result);
-
-  // Members must be accessed while holding this lock to support the creation
-  // and use of `FakeNetworkCostManager` instances on any thread.
-  base::Lock member_lock_;
-
-  // The connection cost to simulate.
-  NetworkChangeNotifier::ConnectionCost connection_cost_
-      GUARDED_BY(member_lock_) =
-          NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN;
-
-  // When FakeNetworkCostManagerEnvironment creates a new
-  // FakeNetworkCostManager, the new FakeNetworkCostManager will simulate this
-  // error.
-  NetworkCostManagerStatus error_status_ GUARDED_BY(member_lock_) =
-      NetworkCostManagerStatus::kOk;
-
-  // Holds the fake implementations of INetworkCostManager constructed through
-  // FakeCoCreateInstance.
-  std::vector<Microsoft::WRL::ComPtr<FakeNetworkCostManager>>
-      fake_network_cost_managers_ GUARDED_BY(member_lock_);
-};
-
-}  // namespace net
-
-#endif  // NET_TEST_WIN_FAKE_NETWORK_COST_MANAGER_H_
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc
index 611198869..1620796 100644
--- a/pdf/pdf_view_web_plugin_unittest.cc
+++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -2214,6 +2214,33 @@
 }
 
 TEST_F(PdfViewWebPluginPrintPreviewTest,
+       HandleResetPrintPreviewModeMessageForPdf) {
+  EXPECT_CALL(*client_ptr_, CreateEngine)
+      .WillOnce([](PDFEngine::Client* client,
+                   PDFiumFormFiller::ScriptOption script_option) {
+        EXPECT_EQ(PDFiumFormFiller::ScriptOption::kNoJavaScript, script_option);
+
+        return std::make_unique<NiceMock<TestPDFiumEngine>>(client);
+      });
+
+  // The UI ID of 1 in the URL is arbitrary.
+  // The page index value of -1, AKA `kCompletePDFIndex`, is required for PDFs.
+  plugin_->OnMessage(ParseMessage(R"({
+    "type": "resetPrintPreviewMode",
+    "url": "chrome-untrusted://print/1/-1/print.pdf",
+    "grayscale": false,
+    "pageCount": 0,
+  })"));
+
+  EXPECT_CALL(*client_ptr_, PostMessage).Times(AnyNumber());
+  EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
+    "type": "printPreviewLoaded",
+  })")));
+  plugin_->DocumentLoadComplete();
+  pdf_receiver_.FlushForTesting();
+}
+
+TEST_F(PdfViewWebPluginPrintPreviewTest,
        HandleResetPrintPreviewModeMessageSetGrayscale) {
   EXPECT_CALL(*client_ptr_, CreateEngine)
       .WillOnce([](PDFEngine::Client* client,
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc
index 9a7b982..272356a4 100644
--- a/printing/backend/cups_printer.cc
+++ b/printing/backend/cups_printer.cc
@@ -6,6 +6,7 @@
 
 #include <cups/cups.h>
 
+#include <cstring>
 #include <string>
 #include <utility>
 
@@ -15,6 +16,7 @@
 #include "printing/backend/cups_connection.h"
 #include "printing/backend/print_backend.h"
 #include "printing/backend/print_backend_consts.h"
+#include "printing/print_job_constants.h"
 
 namespace printing {
 
@@ -79,6 +81,12 @@
     if (!EnsureDestInfo())
       return false;
 
+#if BUILDFLAG(IS_CHROMEOS)
+    // OAuth token passed to CUPS as IPP attribute, see b/200086039.
+    if (name && strcmp(name, kSettingChromeOSAccessOAuthToken) == 0)
+      return true;
+#endif
+
     int supported = cupsCheckDestSupported(cups_http_, destination_.get(),
                                            dest_info_.get(), name, value);
     return supported == 1;
diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc
index d58cedb..b7af10b 100644
--- a/printing/print_job_constants.cc
+++ b/printing/print_job_constants.cc
@@ -6,6 +6,8 @@
 
 #include <limits>
 
+#include "build/build_config.h"
+
 namespace printing {
 
 // True if this is the first preview request.
@@ -20,6 +22,12 @@
 // Capabilities option. Contains the capabilities in CDD format.
 const char kSettingCapabilities[] = "capabilities";
 
+#if BUILDFLAG(IS_CHROMEOS)
+// If set, contains OAuth token that must be used during communication with the
+// printer.
+const char kSettingChromeOSAccessOAuthToken[] = "chromeos-access-oauth-token";
+#endif
+
 // Print job setting 'collate'.
 const char kSettingCollate[] = "collate";
 
diff --git a/printing/print_job_constants.h b/printing/print_job_constants.h
index 8611e0b..ac68dbb0 100644
--- a/printing/print_job_constants.h
+++ b/printing/print_job_constants.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "base/component_export.h"
+#include "build/build_config.h"
 
 namespace printing {
 
@@ -17,6 +18,10 @@
 COMPONENT_EXPORT(PRINTING_BASE) extern const char kPreviewUIID[];
 COMPONENT_EXPORT(PRINTING_BASE)
 extern const char kSettingCapabilities[];
+#if BUILDFLAG(IS_CHROMEOS)
+COMPONENT_EXPORT(PRINTING_BASE)
+extern const char kSettingChromeOSAccessOAuthToken[];
+#endif
 COMPONENT_EXPORT(PRINTING_BASE) extern const char kSettingCollate[];
 COMPONENT_EXPORT(PRINTING_BASE) extern const char kSettingColor[];
 COMPONENT_EXPORT(PRINTING_BASE)
diff --git a/printing/print_settings.cc b/printing/print_settings.cc
index 000ab5c..5d34915 100644
--- a/printing/print_settings.cc
+++ b/printing/print_settings.cc
@@ -279,6 +279,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   send_user_info_ = settings.send_user_info_;
   username_ = settings.username_;
+  oauth_token_ = settings.oauth_token_;
   pin_value_ = settings.pin_value_;
 #endif  // BUILDFLAG(IS_CHROMEOS)
   return *this;
@@ -318,6 +319,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   send_user_info_ = false;
   username_.clear();
+  oauth_token_.clear();
   pin_value_.clear();
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
diff --git a/printing/print_settings.h b/printing/print_settings.h
index 3faff443..f2907c1e 100644
--- a/printing/print_settings.h
+++ b/printing/print_settings.h
@@ -237,6 +237,11 @@
   void set_username(const std::string& username) { username_ = username; }
   const std::string& username() const { return username_; }
 
+  void set_oauth_token(const std::string& oauth_token) {
+    oauth_token_ = oauth_token;
+  }
+  const std::string& oauth_token() const { return oauth_token_; }
+
   void set_pin_value(const std::string& pin_value) { pin_value_ = pin_value; }
   const std::string& pin_value() const { return pin_value_; }
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -334,6 +339,9 @@
   // Username if it's required by the printer.
   std::string username_;
 
+  // OAuth access token if it's required by the printer.
+  std::string oauth_token_;
+
   // PIN code entered by the user.
   std::string pin_value_;
 #endif
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc
index edc2bec..1c7afa9a 100644
--- a/printing/print_settings_conversion.cc
+++ b/printing/print_settings_conversion.cc
@@ -280,6 +280,12 @@
       settings->set_username(*username);
   }
 
+  const std::string* oauth_token =
+      job_settings.FindString(kSettingChromeOSAccessOAuthToken);
+  if (oauth_token) {
+    settings->set_oauth_token(*oauth_token);
+  }
+
   const std::string* pin_value = job_settings.FindString(kSettingPinValue);
   if (pin_value)
     settings->set_pin_value(*pin_value);
diff --git a/printing/print_settings_conversion_unittest.cc b/printing/print_settings_conversion_unittest.cc
index 74e49728..f9f496a 100644
--- a/printing/print_settings_conversion_unittest.cc
+++ b/printing/print_settings_conversion_unittest.cc
@@ -45,6 +45,7 @@
   "previewModifiable": true,
   "sendUserInfo": true,
   "username": "username@domain.net",
+  "chromeos-access-oauth-token": "this is an OAuth access token",
   "pinValue": "0000"
 })";
 
@@ -92,6 +93,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   EXPECT_TRUE(settings->send_user_info());
   EXPECT_EQ("username@domain.net", settings->username());
+  EXPECT_EQ("this is an OAuth access token", settings->oauth_token());
   EXPECT_EQ("0000", settings->pin_value());
 #endif
   EXPECT_EQ(settings->dpi_horizontal(), 300);
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
index 71231e2..7754207 100644
--- a/printing/printing_context_chromeos.cc
+++ b/printing/printing_context_chromeos.cc
@@ -183,6 +183,12 @@
         ConstructOption(it.first, base::JoinString(it.second, ",")));
   }
 
+  // OAuth access token
+  if (!settings.oauth_token().empty()) {
+    options.push_back(ConstructOption(kSettingChromeOSAccessOAuthToken,
+                                      settings.oauth_token()));
+  }
+
   return options;
 }
 
diff --git a/remoting/host/desktop_resizer_x11.cc b/remoting/host/desktop_resizer_x11.cc
index eb5c107..82cc65a5 100644
--- a/remoting/host/desktop_resizer_x11.cc
+++ b/remoting/host/desktop_resizer_x11.cc
@@ -430,9 +430,9 @@
   x11::RandR::ModeInfo mode;
   mode.width = width;
   mode.height = height;
-  mode.dot_clock = 60;
-  mode.htotal = 1;
-  mode.vtotal = 1;
+  mode.dot_clock = 60 * 1e6;
+  mode.htotal = 1000;
+  mode.vtotal = 1000;
   mode.name_len = mode_name.size();
   if (auto reply =
           randr_->CreateMode({root_, mode, mode_name.c_str()}).Sync()) {
diff --git a/remoting/host/mac/permission_utils.mm b/remoting/host/mac/permission_utils.mm
index 556acdb..c7fdcb2b 100644
--- a/remoting/host/mac/permission_utils.mm
+++ b/remoting/host/mac/permission_utils.mm
@@ -62,11 +62,8 @@
   [alert setAlertStyle:NSAlertStyleWarning];
   [alert_window makeKeyWindow];
   if ([alert runModal] == NSAlertFirstButtonReturn) {
-    // Launch the Security and Preferences pane with Accessibility selected.
-    [[NSWorkspace sharedWorkspace]
-        openURL:
-            [NSURL URLWithString:@"x-apple.systempreferences:com.apple."
-                                 @"preference.security?Privacy_Accessibility"]];
+    base::mac::OpenSystemSettingsPane(
+        base::mac::SystemSettingsPane::kPrivacySecurity_Accessibility);
   }
 }
 
@@ -101,11 +98,8 @@
   [alert setAlertStyle:NSAlertStyleWarning];
   [alert_window makeKeyWindow];
   if ([alert runModal] == NSAlertFirstButtonReturn) {
-    // Launch the Security and Preferences pane with Accessibility selected.
-    [[NSWorkspace sharedWorkspace]
-        openURL:
-            [NSURL URLWithString:@"x-apple.systempreferences:com.apple."
-                                 @"preference.security?Privacy_ScreenCapture"]];
+    base::mac::OpenSystemSettingsPane(
+        base::mac::SystemSettingsPane::kPrivacySecurity_ScreenRecording);
   }
 }
 
diff --git a/remoting/host/mac/permission_wizard.mm b/remoting/host/mac/permission_wizard.mm
index bed1c4c..1764ab5 100644
--- a/remoting/host/mac/permission_wizard.mm
+++ b/remoting/host/mac/permission_wizard.mm
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/mac/mac_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -365,18 +366,13 @@
 }
 
 - (void)onLaunchA11y:(id)sender {
-  // Launch the Security and Preferences pane with Accessibility selected.
-  [[NSWorkspace sharedWorkspace]
-      openURL:[NSURL
-                  URLWithString:@"x-apple.systempreferences:com.apple."
-                                @"preference.security?Privacy_Accessibility"]];
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kPrivacySecurity_Accessibility);
 }
 
 - (void)onLaunchScreenRecording:(id)sender {
-  [[NSWorkspace sharedWorkspace]
-      openURL:[NSURL
-                  URLWithString:@"x-apple.systempreferences:com.apple."
-                                @"preference.security?Privacy_ScreenCapture"]];
+  base::mac::OpenSystemSettingsPane(
+      base::mac::SystemSettingsPane::kPrivacySecurity_ScreenRecording);
 }
 
 - (void)onNext:(id)sender {
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc
index 34a9a25e9..23b194d6 100644
--- a/sandbox/policy/features.cc
+++ b/sandbox/policy/features.cc
@@ -19,12 +19,6 @@
 #endif  // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
 
 #if BUILDFLAG(IS_WIN)
-// Emergency "off switch" for new Windows KTM security mitigation,
-// sandbox::MITIGATION_KTM_COMPONENT.
-BASE_FEATURE(kWinSboxDisableKtmComponent,
-             "WinSboxDisableKtmComponent",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Experiment for Windows sandbox security mitigation,
 // sandbox::MITIGATION_EXTENSION_POINT_DISABLE.
 BASE_FEATURE(kWinSboxDisableExtensionPoints,
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h
index 3ca1bf75..069e23f 100644
--- a/sandbox/policy/features.h
+++ b/sandbox/policy/features.h
@@ -20,7 +20,6 @@
 #endif
 
 #if BUILDFLAG(IS_WIN)
-SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxDisableKtmComponent);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxDisableExtensionPoints);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kGpuAppContainer);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kGpuLPAC);
diff --git a/sandbox/policy/win/sandbox_policy_feature_test.cc b/sandbox/policy/win/sandbox_policy_feature_test.cc
index c476fca..f31da33 100644
--- a/sandbox/policy/win/sandbox_policy_feature_test.cc
+++ b/sandbox/policy/win/sandbox_policy_feature_test.cc
@@ -16,11 +16,6 @@
   else
     disabled_features.push_back(features::kRendererAppContainer);
 
-  if (::testing::get<TestParameter::kEnableKtmMitigation>(GetParam()))
-    enabled_features.push_back(features::kWinSboxDisableKtmComponent);
-  else
-    disabled_features.push_back(features::kWinSboxDisableKtmComponent);
-
   feature_list_.InitWithFeatures(enabled_features, disabled_features);
 }
 
@@ -47,7 +42,8 @@
       ::sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE |
       ::sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE |
       ::sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
-      ::sandbox::MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION;
+      ::sandbox::MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION |
+      ::sandbox::MITIGATION_KTM_COMPONENT;
 
 #if !defined(NACL_WIN64)
   // Win32k mitigation is only set on the operating systems it's available on
@@ -55,9 +51,6 @@
     flags = flags | ::sandbox::MITIGATION_WIN32K_DISABLE;
 #endif
 
-  if (::testing::get<TestParameter::kEnableKtmMitigation>(GetParam()))
-    flags = flags | ::sandbox::MITIGATION_KTM_COMPONENT;
-
   return flags;
 }
 
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index a338a80..a885e8cb 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -744,10 +744,7 @@
       MITIGATION_DEP_NO_ATL_THUNK | MITIGATION_EXTENSION_POINT_DISABLE |
       MITIGATION_SEHOP | MITIGATION_NONSYSTEM_FONT_DISABLE |
       MITIGATION_IMAGE_LOAD_NO_REMOTE | MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
-      MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION;
-
-  if (base::FeatureList::IsEnabled(features::kWinSboxDisableKtmComponent))
-    mitigations |= MITIGATION_KTM_COMPONENT;
+      MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION | MITIGATION_KTM_COMPONENT;
 
   // CET is enabled with the CETCOMPAT bit on chrome.exe so must be
   // disabled for processes we know are not compatible.
diff --git a/services/image_annotation/annotator.cc b/services/image_annotation/annotator.cc
index 3043bfca..a36ba13f 100644
--- a/services/image_annotation/annotator.cc
+++ b/services/image_annotation/annotator.cc
@@ -61,8 +61,16 @@
   // For every language other than "zh" (Chinese), return only the language
   // and strip the locale. Image descriptions don't changed based on locale,
   // but zh-CN and zh-TW use different character sets.
-  if (tokens.size() == 1 || language_only != "zh")
+  if (tokens.size() == 1 || language_only != "zh") {
+    // For the case of Hebrew, Chrome uses "he" but vss uses "iw" internally.
+    // For Norwegian, vss uses "no" for all variants.
+    if (language_only == "he") {
+      return "iw";
+    } else if (language_only == "nb" || language_only == "nn") {
+      return "no";
+    }
     return language_only;
+  }
 
   // Normalize the locale to uppercase.
   std::string locale_only = base::ToUpperASCII(tokens[1]);
@@ -454,7 +462,8 @@
       api_key_(std::move(api_key)),
       batch_size_(batch_size),
       min_ocr_confidence_(min_ocr_confidence),
-      server_languages_({"de", "en", "es", "fr", "hi", "it"}) {
+      server_languages_({"cs", "de", "en", "es", "fi", "fr", "hi", "hr", "id",
+                         "it", "iw", "nl", "no", "pt", "ru", "sv", "tr"}) {
   server_request_timer_ = std::make_unique<base::RepeatingTimer>(
       FROM_HERE, throttle,
       base::BindRepeating(&Annotator::SendRequestBatchToServer,
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 7ffc102..f84ab193 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -34,7 +34,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -100,7 +100,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -175,7 +175,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -234,7 +234,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -293,7 +293,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -352,7 +352,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -411,7 +411,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -470,7 +470,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -529,7 +529,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -588,7 +588,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -692,7 +692,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -751,7 +751,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -817,7 +817,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -886,7 +886,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -946,7 +946,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -1008,7 +1008,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -1068,7 +1068,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -1129,7 +1129,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -1194,7 +1194,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 44cb0686..56ecf6c 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -8213,7 +8213,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8273,7 +8273,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8334,7 +8334,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8393,7 +8393,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8446,7 +8446,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8497,7 +8497,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8556,7 +8556,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8615,7 +8615,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8674,7 +8674,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8734,7 +8734,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8794,7 +8794,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8853,7 +8853,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8913,7 +8913,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -8972,7 +8972,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9032,7 +9032,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9091,7 +9091,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9161,7 +9161,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9230,7 +9230,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9295,7 +9295,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9355,7 +9355,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9416,7 +9416,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9540,7 +9540,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9600,7 +9600,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9661,7 +9661,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9720,7 +9720,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9779,7 +9779,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9838,7 +9838,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9897,7 +9897,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -9956,7 +9956,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10015,7 +10015,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10074,7 +10074,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10136,7 +10136,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10195,7 +10195,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10254,7 +10254,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10313,7 +10313,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10372,7 +10372,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10431,7 +10431,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10490,7 +10490,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10549,7 +10549,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10608,7 +10608,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10668,7 +10668,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10727,7 +10727,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10786,7 +10786,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10845,7 +10845,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10904,7 +10904,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -10963,7 +10963,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11022,7 +11022,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11082,7 +11082,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11142,7 +11142,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11201,7 +11201,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11260,7 +11260,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11319,7 +11319,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11378,7 +11378,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11437,7 +11437,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11496,7 +11496,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11555,7 +11555,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11614,7 +11614,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11673,7 +11673,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11733,7 +11733,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11792,7 +11792,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11852,7 +11852,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11920,7 +11920,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -11992,7 +11992,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12052,7 +12052,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12111,7 +12111,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12174,7 +12174,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12234,7 +12234,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12295,7 +12295,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12355,7 +12355,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12408,7 +12408,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12460,7 +12460,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12519,7 +12519,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12578,7 +12578,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12637,7 +12637,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12697,7 +12697,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12757,7 +12757,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12816,7 +12816,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12876,7 +12876,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12935,7 +12935,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -12995,7 +12995,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13054,7 +13054,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13124,7 +13124,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13193,7 +13193,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13259,7 +13259,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13319,7 +13319,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13379,7 +13379,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13440,7 +13440,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13501,7 +13501,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13561,7 +13561,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13622,7 +13622,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13681,7 +13681,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13741,7 +13741,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13800,7 +13800,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13859,7 +13859,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13918,7 +13918,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -13977,7 +13977,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14036,7 +14036,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14098,7 +14098,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14158,7 +14158,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14217,7 +14217,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14276,7 +14276,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14335,7 +14335,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14394,7 +14394,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14453,7 +14453,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14512,7 +14512,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14571,7 +14571,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14631,7 +14631,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14690,7 +14690,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14749,7 +14749,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14808,7 +14808,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14867,7 +14867,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14926,7 +14926,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -14985,7 +14985,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15046,7 +15046,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15106,7 +15106,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15166,7 +15166,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15225,7 +15225,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15284,7 +15284,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15343,7 +15343,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15402,7 +15402,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15461,7 +15461,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15520,7 +15520,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15579,7 +15579,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15638,7 +15638,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15697,7 +15697,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15757,7 +15757,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15816,7 +15816,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15875,7 +15875,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -15943,7 +15943,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16015,7 +16015,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16075,7 +16075,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16134,7 +16134,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16193,7 +16193,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16256,7 +16256,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16315,7 +16315,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16376,7 +16376,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16435,7 +16435,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16488,7 +16488,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16538,7 +16538,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16597,7 +16597,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16656,7 +16656,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16715,7 +16715,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16775,7 +16775,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16835,7 +16835,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16894,7 +16894,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -16954,7 +16954,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17013,7 +17013,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17072,7 +17072,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17131,7 +17131,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17197,7 +17197,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17265,7 +17265,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17330,7 +17330,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17390,7 +17390,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17449,7 +17449,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17509,7 +17509,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17569,7 +17569,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17629,7 +17629,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17689,7 +17689,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17748,7 +17748,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17807,7 +17807,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17866,7 +17866,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17925,7 +17925,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17984,7 +17984,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18043,7 +18043,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18102,7 +18102,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18163,7 +18163,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18222,7 +18222,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18281,7 +18281,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18340,7 +18340,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18399,7 +18399,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18458,7 +18458,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18517,7 +18517,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18576,7 +18576,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18635,7 +18635,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18694,7 +18694,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18753,7 +18753,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18812,7 +18812,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18871,7 +18871,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18930,7 +18930,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -18989,7 +18989,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19048,7 +19048,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19108,7 +19108,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19167,7 +19167,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19226,7 +19226,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19285,7 +19285,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19344,7 +19344,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19403,7 +19403,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19462,7 +19462,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19521,7 +19521,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19580,7 +19580,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19639,7 +19639,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19698,7 +19698,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19757,7 +19757,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19817,7 +19817,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19876,7 +19876,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19935,7 +19935,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -19995,7 +19995,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -20054,7 +20054,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -20113,7 +20113,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23290,7 +23290,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23358,7 +23358,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23427,7 +23427,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23489,7 +23489,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23552,7 +23552,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23614,7 +23614,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -23677,7 +23677,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24277,7 +24277,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24336,7 +24336,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24395,7 +24395,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24454,7 +24454,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24513,7 +24513,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24572,7 +24572,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24632,7 +24632,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24696,7 +24696,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24755,7 +24755,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24814,7 +24814,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24873,7 +24873,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24932,7 +24932,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -24991,7 +24991,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25050,7 +25050,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25114,7 +25114,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25173,7 +25173,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25232,7 +25232,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25291,7 +25291,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25350,7 +25350,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25409,7 +25409,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25469,7 +25469,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25533,7 +25533,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25592,7 +25592,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25651,7 +25651,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25710,7 +25710,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25769,7 +25769,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25828,7 +25828,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25887,7 +25887,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -25951,7 +25951,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26010,7 +26010,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26069,7 +26069,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26128,7 +26128,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26187,7 +26187,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26246,7 +26246,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26306,7 +26306,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26370,7 +26370,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26429,7 +26429,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26488,7 +26488,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26547,7 +26547,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26606,7 +26606,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26665,7 +26665,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26725,7 +26725,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26849,7 +26849,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26908,7 +26908,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -26967,7 +26967,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27026,7 +27026,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27085,7 +27085,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27144,7 +27144,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27204,7 +27204,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27273,7 +27273,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27333,7 +27333,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27362,7 +27362,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
+          "shards": 9
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -27395,7 +27395,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27455,7 +27455,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27509,7 +27509,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27560,7 +27560,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27620,7 +27620,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27680,7 +27680,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27741,7 +27741,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27802,7 +27802,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27863,7 +27863,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27923,7 +27923,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -27984,7 +27984,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28044,7 +28044,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28104,7 +28104,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28170,7 +28170,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28230,7 +28230,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28370,7 +28370,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28439,7 +28439,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28505,7 +28505,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28576,7 +28576,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28637,7 +28637,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28698,7 +28698,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -28949,7 +28949,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29010,7 +29010,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29070,7 +29070,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29130,7 +29130,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29190,7 +29190,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29250,7 +29250,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29310,7 +29310,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29370,7 +29370,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29430,7 +29430,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29493,7 +29493,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29553,7 +29553,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29613,7 +29613,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29673,7 +29673,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29733,7 +29733,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29793,7 +29793,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29853,7 +29853,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29913,7 +29913,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -29973,7 +29973,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30033,7 +30033,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30093,7 +30093,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30153,7 +30153,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30213,7 +30213,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30273,7 +30273,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30333,7 +30333,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30393,7 +30393,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30455,7 +30455,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30516,7 +30516,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30577,7 +30577,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30637,7 +30637,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30697,7 +30697,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30757,7 +30757,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30817,7 +30817,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30877,7 +30877,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30937,7 +30937,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -30997,7 +30997,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31057,7 +31057,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31117,7 +31117,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31178,7 +31178,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31238,7 +31238,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31306,7 +31306,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31367,7 +31367,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31432,7 +31432,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31493,7 +31493,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31553,7 +31553,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31613,7 +31613,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31673,7 +31673,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31719,7 +31719,7 @@
               "device_os_flavor": null,
               "device_playstore_version": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31770,7 +31770,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31820,7 +31820,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -31867,7 +31867,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36224,7 +36224,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36345,7 +36345,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36404,7 +36404,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36457,7 +36457,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36507,7 +36507,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36566,7 +36566,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36625,7 +36625,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36684,7 +36684,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36744,7 +36744,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36804,7 +36804,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36863,7 +36863,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36923,7 +36923,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -36982,7 +36982,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37042,7 +37042,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37101,7 +37101,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37160,7 +37160,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37227,7 +37227,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37296,7 +37296,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37361,7 +37361,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37421,7 +37421,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37480,7 +37480,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37541,7 +37541,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37602,7 +37602,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37662,7 +37662,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37722,7 +37722,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37781,7 +37781,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37840,7 +37840,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37899,7 +37899,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -37958,7 +37958,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38017,7 +38017,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38076,7 +38076,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38135,7 +38135,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38197,7 +38197,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38256,7 +38256,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38315,7 +38315,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38374,7 +38374,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38433,7 +38433,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38492,7 +38492,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38551,7 +38551,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38610,7 +38610,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38669,7 +38669,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38728,7 +38728,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38787,7 +38787,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38846,7 +38846,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38905,7 +38905,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -38964,7 +38964,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39023,7 +39023,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39083,7 +39083,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39144,7 +39144,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39203,7 +39203,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39262,7 +39262,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39321,7 +39321,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39380,7 +39380,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39439,7 +39439,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39498,7 +39498,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39557,7 +39557,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39616,7 +39616,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39675,7 +39675,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39734,7 +39734,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39794,7 +39794,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39853,7 +39853,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39919,7 +39919,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -39980,7 +39980,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -40040,7 +40040,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -40099,7 +40099,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -40158,7 +40158,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -40243,7 +40243,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -40315,7 +40315,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "n1-standard-4|e2-standard-4",
+              "machine_type": "n1-standard-4|e2-standard-4|n2-standard-4",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index ec59a10..9a3ce1df 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5813,9 +5813,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5827,8 +5827,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -5979,9 +5979,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5993,8 +5993,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -6130,9 +6130,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6144,8 +6144,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 86d2366..e0018cf 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -17597,7 +17597,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17653,7 +17653,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17708,7 +17708,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17764,7 +17764,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -17819,7 +17819,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "machine_type": "e2-standard-8",
+              "machine_type": "e2-standard-8|n2-standard-8",
               "os": "Ubuntu-18.04",
               "pool": "chromium.tests.avd"
             }
@@ -88143,9 +88143,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -88157,8 +88157,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -88279,9 +88279,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -88293,8 +88293,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -88405,9 +88405,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -88419,8 +88419,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -89760,9 +89760,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -89773,8 +89773,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -89926,9 +89926,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -89939,8 +89939,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -90077,9 +90077,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -90090,8 +90090,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -91612,9 +91612,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -91625,8 +91625,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -91778,9 +91778,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -91791,8 +91791,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -91929,9 +91929,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -91942,8 +91942,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -92741,9 +92741,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -92754,8 +92754,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 6b091d21..3ff3ee8 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -20360,10 +20360,10 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -20375,8 +20375,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -20534,10 +20534,10 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -20549,8 +20549,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
@@ -20690,10 +20690,10 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always"
         ],
-        "description": "Run with ash-chrome version 109.0.5394.0",
+        "description": "Run with ash-chrome version 109.0.5395.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -20705,8 +20705,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v109.0.5394.0",
-              "revision": "version:109.0.5394.0"
+              "location": "lacros_version_skew_tests_v109.0.5395.0",
+              "revision": "version:109.0.5395.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 1c725ec..dba6b9f 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -3708,7 +3708,8 @@
             }
           ],
           "quickrun_shards": 3,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
         },
         "test": "sync_integration_tests",
         "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 92d5d8b..15d7847 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -445,7 +445,7 @@
         'device_os': None,
         'device_type': None,
         'pool': 'chromium.tests.avd',
-        'machine_type': 'n1-standard-4|e2-standard-4',
+        'machine_type': 'n1-standard-4|e2-standard-4|n2-standard-4',
       },
     },
   },
@@ -455,7 +455,7 @@
         'device_os': None,
         'device_type': None,
         'pool': 'chromium.tests.avd',
-        'machine_type': 'e2-standard-8',
+        'machine_type': 'e2-standard-8|n2-standard-8',
       },
     },
   },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 76dbaa3c..c9f5e248 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -50,7 +50,7 @@
       },
       'android-nougat-x86-rel': {
         'swarming': {
-          'shards': 3,
+          'shards': 9,
         },
       },
       'android-pie-arm64-rel': {
@@ -3278,6 +3278,7 @@
       },
       'Win10 Tests x64': {
         'swarming': {
+          'shards': 3,
           'quickrun_shards': 3,
         },
       },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index d5bc05e..769f765 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5394.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v109.0.5395.0/test_ash_chrome',
     ],
-    'description': 'Run with ash-chrome version 109.0.5394.0',
+    'description': 'Run with ash-chrome version 109.0.5395.0',
     'identifier': 'Lacros version skew testing ash canary',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v109.0.5394.0',
-          'revision': 'version:109.0.5394.0',
+          'location': 'lacros_version_skew_tests_v109.0.5395.0',
+          'revision': 'version:109.0.5395.0',
         },
       ],
     },
diff --git a/testing/scripts/run_finch_smoke_tests_android.py b/testing/scripts/run_finch_smoke_tests_android.py
index 69caa166..f9411685 100755
--- a/testing/scripts/run_finch_smoke_tests_android.py
+++ b/testing/scripts/run_finch_smoke_tests_android.py
@@ -220,6 +220,7 @@
     rest_args = super(FinchTestCase, self).rest_args
 
     rest_args.extend([
+      '--webdriver-arg=--disable-build-check',
       '--device-serial',
       self._device.serial,
       '--webdriver-binary',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ed6f94e..b30acac9 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1599,6 +1599,26 @@
             ]
         }
     ],
+    "AvoidRasterDuringElasticOverscroll": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AvoidRasterDuringElasticOverscroll"
+                    ]
+                }
+            ]
+        }
+    ],
     "AvoidUnnecessaryBeforeUnloadCheckSync": [
         {
             "platforms": [
@@ -3442,6 +3462,21 @@
             ]
         }
     ],
+    "DefaultOfflineExperience": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PWAsDefaultOfflinePage"
+                    ]
+                }
+            ]
+        }
+    ],
     "DefaultPassthroughCommandDecoder": [
         {
             "platforms": [
@@ -4749,7 +4784,20 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "Enabled (Fullscreen)",
+                    "params": {
+                        "ios-new-post-restore-experience": "false"
+                    },
+                    "enable_features": [
+                        "FullscreenPromosManager",
+                        "IOSNewPostRestoreExperience"
+                    ]
+                },
+                {
+                    "name": "Enabled (Alert)",
+                    "params": {
+                        "ios-new-post-restore-experience": "true"
+                    },
                     "enable_features": [
                         "FullscreenPromosManager",
                         "IOSNewPostRestoreExperience"
@@ -4952,7 +5000,6 @@
                     "name": "Enabled_081722",
                     "params": {
                         "enableCameraAssistedSearchOnTablet": "true",
-                        "enableCameraAssistedSearchOnTabletWidget": "true",
                         "enableContextMenuSearchOnTablet": "true"
                     },
                     "enable_features": [
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 95ea815e..aae649c7 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -93,6 +93,26 @@
   ]
 }
 
+blink_python_runner("make_runtime_feature_state_contexts_impl") {
+  script =
+      "../renderer/build/scripts/make_runtime_feature_state_contexts_impl.py"
+
+  inputs = scripts_for_json5_files + [
+             "../renderer/platform/runtime_enabled_features.json5",
+             "../renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl",
+           ]
+
+  outputs = [ "$root_gen_dir/third_party/blink/common/runtime_feature_state/runtime_feature_state_context.cc" ]
+
+  args = [
+    rebase_path("../renderer/platform/runtime_enabled_features.json5",
+                root_build_dir),
+    "--output_dir",
+    rebase_path("$root_gen_dir/third_party/blink/common/runtime_feature_state/",
+                root_build_dir),
+  ]
+}
+
 config("blink_common_implementation") {
   defines = [ "BLINK_COMMON_IMPLEMENTATION=1" ]
 }
@@ -261,12 +281,14 @@
   sources += get_target_outputs(":make_generated_features")
   sources += get_target_outputs(":make_generated_origin_trials")
   sources += get_target_outputs(":make_generated_permissions_policy_features")
+  sources += get_target_outputs(":make_runtime_feature_state_contexts_impl")
 
   public_deps = [
     ":make_generated_document_policy_features",
     ":make_generated_features",
     ":make_generated_origin_trials",
     ":make_generated_permissions_policy_features",
+    ":make_runtime_feature_state_contexts_impl",
     "//third_party/blink/common/privacy_budget:privacy_budget",
     "//third_party/blink/public/common:common_export",
     "//third_party/blink/public/common:headers",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 1a9cfd0..83d32afc 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -56,6 +56,30 @@
   ]
 }
 
+blink_python_runner("make_runtime_feature_state_contexts_headers") {
+  script = "../../renderer/build/scripts/make_runtime_feature_state_contexts_headers.py"
+
+  inputs = scripts_for_json5_files + [
+             "../../renderer/platform/runtime_enabled_features.json5",
+             "../../renderer/build/scripts/templates/runtime_feature_state_context.h.tmpl",
+             "../../renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl",
+           ]
+
+  outputs = [
+    "$root_gen_dir/third_party/blink/public/common/runtime_feature_state/runtime_feature_state_context.h",
+    "$root_gen_dir/third_party/blink/public/common/runtime_feature_state/runtime_feature_state_read_context.h",
+  ]
+
+  args = [
+    rebase_path("../../renderer/platform/runtime_enabled_features.json5",
+                root_build_dir),
+    "--output_dir",
+    rebase_path(
+        "$root_gen_dir/third_party/blink/public/common/runtime_feature_state/",
+        root_build_dir),
+  ]
+}
+
 # Public common API headers, mojom and libraries that can be linked and
 # referenced both by browser-side and renderer-side components.
 component("common") {
@@ -290,10 +314,12 @@
 
   sources += get_target_outputs(":make_generated_origin_trial_feature")
   sources += get_target_outputs(":make_generated_permissions_policy_helper")
+  sources += get_target_outputs(":make_runtime_feature_state_contexts_headers")
 
   public_deps = [
     ":make_generated_origin_trial_feature",
     ":make_generated_permissions_policy_helper",
+    ":make_runtime_feature_state_contexts_headers",
     "//base",
     "//mojo/public/cpp/bindings",
     "//services/metrics/public/cpp:metrics_cpp",
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index b275c7e..d5f40bb 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -1054,6 +1054,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_playback_destination.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_sample_format.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_sample_format.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_sink_type.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_sink_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_automation_rate.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_automation_rate.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_avc_bitstream_format.cc",
@@ -1521,6 +1523,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_processing_event.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_scheduled_source_node.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_scheduled_source_node.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_sink_info.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_sink_info.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_track.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_track.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_audio_worklet.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index a0df5701..ce2a17e 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -774,6 +774,7 @@
           "//third_party/blink/renderer/modules/webaudio/audio_processing_event.idl",
           "//third_party/blink/renderer/modules/webaudio/audio_processing_event_init.idl",
           "//third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.idl",
+          "//third_party/blink/renderer/modules/webaudio/audio_sink_info.idl",
           "//third_party/blink/renderer/modules/webaudio/audio_timestamp.idl",
           "//third_party/blink/renderer/modules/webaudio/audio_worklet.idl",
           "//third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.idl",
diff --git a/third_party/blink/renderer/build/scripts/make_runtime_feature_state_contexts_headers.py b/third_party/blink/renderer/build/scripts/make_runtime_feature_state_contexts_headers.py
new file mode 100755
index 0000000..093edc5
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/make_runtime_feature_state_contexts_headers.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json5_generator
+import make_runtime_features
+import make_runtime_features_utilities as util
+import template_expander
+
+
+class RunTimeFeatureStateContextHeaderWriter(
+        make_runtime_features.BaseRuntimeFeatureWriter):
+    file_basename = "runtime_feature_state_context"
+
+    def __init__(self, json5_file_path, output_dir):
+        super(RunTimeFeatureStateContextHeaderWriter,
+              self).__init__(json5_file_path, output_dir)
+        self._outputs = {
+            (self.file_basename + '.h'): self.generate_header,
+        }
+
+        self._browser_read_access_features = util.browser_read_access(
+            self._features)
+        self._browser_write_access_features = util.browser_write_access(
+            self._features)
+
+    def _template_inputs(self):
+        return {
+            'features': self._features,
+            'browser_read_access_features': self._browser_read_access_features,
+            'browser_write_access_features':
+            self._browser_write_access_features,
+            'platforms': self._platforms(),
+            'input_files': self._input_files,
+            'header_guard': self._header_guard,
+        }
+
+    @template_expander.use_jinja(f'templates/{file_basename}.h.tmpl')
+    def generate_header(self):
+        return self._template_inputs()
+
+
+class RunTimeFeatureStateReadContextHeaderWriter(
+        make_runtime_features.BaseRuntimeFeatureWriter):
+    file_basename = "runtime_feature_state_read_context"
+
+    def __init__(self, json5_file_path, output_dir):
+        super(RunTimeFeatureStateReadContextHeaderWriter,
+              self).__init__(json5_file_path, output_dir)
+        self._outputs = {
+            (self.file_basename + '.h'): self.generate_header,
+        }
+        self._browser_read_access_features = util.browser_read_access(
+            self._features)
+        self._browser_write_access_features = util.browser_write_access(
+            self._features)
+
+    def _template_inputs(self):
+        return {
+            'features': self._features,
+            'browser_read_access_features': self._browser_read_access_features,
+            'browser_write_access_features':
+            self._browser_write_access_features,
+            'platforms': self._platforms(),
+            'input_files': self._input_files,
+            'header_guard': self._header_guard,
+        }
+
+    @template_expander.use_jinja(f'templates/{file_basename}.h.tmpl')
+    def generate_header(self):
+        return self._template_inputs()
+
+
+if __name__ == '__main__':
+    json5_generator.Maker(RunTimeFeatureStateReadContextHeaderWriter).main()
+    json5_generator.Maker(RunTimeFeatureStateContextHeaderWriter).main()
diff --git a/third_party/blink/renderer/build/scripts/make_runtime_feature_state_contexts_impl.py b/third_party/blink/renderer/build/scripts/make_runtime_feature_state_contexts_impl.py
new file mode 100755
index 0000000..e7f4e0a8
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/make_runtime_feature_state_contexts_impl.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json5_generator
+import make_runtime_features
+import make_runtime_features_utilities as util
+import template_expander
+
+
+class RunTimeFeatureStateContextImplWriter(
+        make_runtime_features.BaseRuntimeFeatureWriter):
+    file_basename = "runtime_feature_state_context"
+
+    def __init__(self, json5_file_path, output_dir):
+        super(RunTimeFeatureStateContextImplWriter,
+              self).__init__(json5_file_path, output_dir)
+        self._outputs = {
+            (self.file_basename + '.cc'): self.generate_implementation,
+        }
+
+        self._browser_read_access_features = util.browser_read_access(
+            self._features)
+
+    def _template_inputs(self):
+        return {
+            'features': self._features,
+            'browser_read_access_features': self._browser_read_access_features,
+            'platforms': self._platforms(),
+            'input_files': self._input_files,
+            'header_guard': self._header_guard,
+        }
+
+    @template_expander.use_jinja(f'templates/{file_basename}.cc.tmpl')
+    def generate_implementation(self):
+        return self._template_inputs()
+
+
+if __name__ == '__main__':
+    json5_generator.Maker(RunTimeFeatureStateContextImplWriter).main()
diff --git a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl
new file mode 100644
index 0000000..80a9a1a2
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.cc.tmpl
@@ -0,0 +1,26 @@
+{% from 'templates/macros.tmpl' import license, source_files_for_generated_file %}
+{{license()}}
+
+{{source_files_for_generated_file(template_file, input_files)}}
+
+#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_context.h"
+
+#include "base/containers/flat_map.h"
+#include "third_party/blink/public/mojom/runtime_feature_state/runtime_feature_state.mojom-shared.h"
+#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_read_context.h"
+
+namespace blink {
+
+void RuntimeFeatureStateContext::PopulateInitialValues() {
+  // Write access implies read, so we are populating all inital values
+  initial_values_.reserve({{browser_read_access_features|length()}});
+
+  {% for feature in browser_read_access_features %}
+  // TODO(crbug.com/1377000): fetch current value.
+  initial_values_.insert(
+      {blink::mojom::RuntimeFeatureState::k{{feature.name}},
+        false});
+  {% endfor %}
+}
+
+}  // namespace blink
\ No newline at end of file
diff --git a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.h.tmpl b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.h.tmpl
new file mode 100644
index 0000000..e8ba619
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_context.h.tmpl
@@ -0,0 +1,45 @@
+{% from 'templates/macros.tmpl' import license, source_files_for_generated_file %}
+{{license()}}
+
+{{source_files_for_generated_file(template_file, input_files)}}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "base/containers/flat_map.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/mojom/runtime_feature_state/runtime_feature_state.mojom-shared.h"
+#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_read_context.h"
+
+namespace blink {
+
+class BLINK_COMMON_EXPORT RuntimeFeatureStateContext
+    : public RuntimeFeatureStateReadContext {
+ public:
+  explicit RuntimeFeatureStateContext() { PopulateInitialValues(); }
+
+  RuntimeFeatureStateReadContext GetRuntimeFeatureStateReadContext() const {
+    return static_cast<RuntimeFeatureStateReadContext>(*this);
+  }
+
+  // Note: The Is*Enabled() functions are defined in the parent class.
+
+  {% for feature in browser_write_access_features %}
+  void Set{{feature.name}}Enabled(bool enabled) {
+    return SetIsEnabled(
+        blink::mojom::RuntimeFeatureState::k{{feature.name}},
+        enabled);
+  }
+  {% endfor %}
+
+ private:
+  void PopulateInitialValues();
+
+  void SetIsEnabled(blink::mojom::RuntimeFeatureState feature, bool enabled) {
+    feature_overrides_[feature] = enabled;
+  }
+};
+
+}  // namespace blink
+
+#endif // {{header_guard}}
diff --git a/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl
new file mode 100644
index 0000000..06b2f49
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/templates/runtime_feature_state_read_context.h.tmpl
@@ -0,0 +1,55 @@
+{% from 'templates/macros.tmpl' import license, source_files_for_generated_file %}
+{{license()}}
+
+{{source_files_for_generated_file(template_file, input_files)}}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "base/containers/flat_map.h"
+#include "base/notreached.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/mojom/runtime_feature_state/runtime_feature_state.mojom-shared.h"
+
+namespace blink {
+
+class BLINK_COMMON_EXPORT RuntimeFeatureStateReadContext {
+ public:
+  // You probably don't want to instantiate this class directly, use
+  // RuntimeFeatureStateContext instead.
+  RuntimeFeatureStateReadContext() = default;
+
+  const base::flat_map<blink::mojom::RuntimeFeatureState, bool>&
+  GetFeatureOverrides() const {
+    return feature_overrides_;
+  }
+
+  {% for feature in browser_read_access_features %}
+  bool Is{{feature.name}}Enabled() {
+    return IsEnabled(
+        blink::mojom::RuntimeFeatureState::k{{feature.name}});
+  }
+  {% endfor %}
+
+ protected:
+  bool IsEnabled(blink::mojom::RuntimeFeatureState feature) const {
+    auto override_it = feature_overrides_.find(feature);
+    if (override_it != feature_overrides_.end())
+      return override_it->second;
+
+    auto initial_it = initial_values_.find(feature);
+    DCHECK(initial_it != initial_values_.end());
+    return initial_it->second;
+  }
+
+  // Sparse map of overrides collected during initial navigation. This map
+  // will be attached to the navigation on commit.
+  base::flat_map<blink::mojom::RuntimeFeatureState, bool> feature_overrides_;
+
+  // Values for all read/write features on context creation.
+  base::flat_map<blink::mojom::RuntimeFeatureState, bool> initial_values_;
+};
+
+}  // namespace blink
+
+#endif // {{header_guard}}
diff --git a/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc
index b68782b..ab0e551 100644
--- a/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc
@@ -149,7 +149,7 @@
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
   const auto& aspect_ratio = To<InterpolableAspectRatio>(interpolable_value);
-  state.Style()->SetAspectRatio(StyleAspectRatio(
+  state.StyleBuilder().SetAspectRatio(StyleAspectRatio(
       To<CSSAspectRatioNonInterpolableValue>(non_interpolable_value)
           ->GetAspectRatioType(),
       aspect_ratio.GetRatio()));
diff --git a/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
index 82fc97d3..3463107 100644
--- a/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
@@ -186,11 +186,11 @@
           state.CssToLengthConversionData());
   switch (CssProperty().PropertyID()) {
     case CSSPropertyID::kShapeOutside:
-      state.Style()->SetShapeOutside(MakeGarbageCollected<ShapeValue>(
+      state.StyleBuilder().SetShapeOutside(MakeGarbageCollected<ShapeValue>(
           std::move(shape), CSSBoxType::kMissing));
       break;
     case CSSPropertyID::kClipPath:
-      state.Style()->SetClipPath(
+      state.StyleBuilder().SetClipPath(
           ShapeClipPathOperation::Create(std::move(shape)));
       break;
     case CSSPropertyID::kObjectViewBox:
diff --git a/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
index 0a6882d..404b788 100644
--- a/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
@@ -292,7 +292,7 @@
         .CreateLength(state.CssToLengthConversionData(),
                       Length::ValueRange::kAll);
   };
-  state.Style()->SetClip(
+  state.StyleBuilder().SetClip(
       LengthBox(convert_index(autos.is_top_auto, kClipTop),
                 convert_index(autos.is_right_auto, kClipRight),
                 convert_index(autos.is_bottom_auto, kClipBottom),
diff --git a/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc
index 43e7fe8..fd66b21 100644
--- a/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc
@@ -35,14 +35,14 @@
 }
 
 void SetFilterList(const CSSProperty& property,
-                   ComputedStyle& style,
+                   ComputedStyleBuilder& builder,
                    const FilterOperations& filter_operations) {
   switch (property.PropertyID()) {
     case CSSPropertyID::kBackdropFilter:
-      style.SetBackdropFilter(filter_operations);
+      builder.SetBackdropFilter(filter_operations);
       break;
     case CSSPropertyID::kFilter:
-      style.SetFilter(filter_operations);
+      builder.SetFilter(filter_operations);
       break;
     default:
       NOTREACHED();
@@ -264,7 +264,8 @@
         To<InterpolableFilter>(interpolable_list.Get(i))
             ->CreateFilterOperation(state));
   }
-  SetFilterList(CssProperty(), *state.Style(), std::move(filter_operations));
+  SetFilterList(CssProperty(), state.StyleBuilder(),
+                std::move(filter_operations));
 }
 
 InterpolationValue
diff --git a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
index 3c9a862..d4de67a 100644
--- a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
@@ -274,7 +274,7 @@
       state.Style()->SetBorderImageSource(image);
       break;
     case CSSPropertyID::kListStyleImage:
-      state.Style()->SetListStyleImage(image);
+      state.StyleBuilder().SetListStyleImage(image);
       break;
     case CSSPropertyID::kWebkitMaskBoxImageSource:
       state.Style()->SetMaskBoxImageSource(image);
diff --git a/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc
index 55af071..cf200a8 100644
--- a/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc
@@ -121,12 +121,12 @@
 }
 
 void CSSIntrinsicLengthInterpolationType::SetIntrinsicDimension(
-    ComputedStyle* style,
+    ComputedStyleBuilder& builder,
     const absl::optional<StyleIntrinsicLength>& dimension) const {
   if (CssProperty().PropertyID() == CSSPropertyID::kContainIntrinsicWidth)
-    style->SetContainIntrinsicWidth(dimension);
+    builder.SetContainIntrinsicWidth(dimension);
   else
-    style->SetContainIntrinsicHeight(dimension);
+    builder.SetContainIntrinsicHeight(dimension);
 }
 
 InterpolationValue CSSIntrinsicLengthInterpolationType::MaybeConvertNeutral(
@@ -196,10 +196,10 @@
   const auto* non_interpolable =
       To<CSSIntrinsicLengthNonInterpolableValue>(non_interpolable_value);
   if (non_interpolable->IsNone()) {
-    SetIntrinsicDimension(state.Style(), absl::nullopt);
+    SetIntrinsicDimension(state.StyleBuilder(), absl::nullopt);
   } else {
     SetIntrinsicDimension(
-        state.Style(),
+        state.StyleBuilder(),
         StyleIntrinsicLength(
             non_interpolable->HasAuto(),
             interpolable.CreateLength(state.CssToLengthConversionData(),
diff --git a/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.h b/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.h
index 1728e705..595887e5 100644
--- a/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.h
@@ -44,7 +44,7 @@
   absl::optional<StyleIntrinsicLength> GetIntrinsicDimension(
       const ComputedStyle&) const;
   void SetIntrinsicDimension(
-      ComputedStyle*,
+      ComputedStyleBuilder&,
       const absl::optional<StyleIntrinsicLength>& dimension) const;
 
   InterpolationValue MaybeConvertNeutral(const InterpolationValue& underlying,
diff --git a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
index cc6f6ca..a53c19c 100644
--- a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
@@ -142,8 +142,7 @@
       state.CssToLengthConversionData().CopyWithAdjustedZoom(zoom);
   Length length = To<InterpolableLength>(interpolable_value)
                       .CreateLength(conversion_data, value_range_);
-  if (LengthPropertyFunctions::SetLength(CssProperty(), style, builder,
-                                         length)) {
+  if (LengthPropertyFunctions::SetLength(CssProperty(), builder, length)) {
 #if DCHECK_IS_ON()
     scoped_refptr<const ComputedStyle> before_style = builder.ToStyle();
     // Assert that setting the length on ComputedStyle directly is identical to
diff --git a/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc
index 5a7c9011..d42f172 100644
--- a/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc
@@ -42,6 +42,7 @@
 // Set the property to the given path() value.
 void SetPath(const CSSProperty& property,
              ComputedStyle& style,
+             ComputedStyleBuilder& builder,
              scoped_refptr<blink::StylePath> path) {
   switch (property.PropertyID()) {
     case CSSPropertyID::kD:
@@ -51,7 +52,7 @@
       style.SetOffsetPath(std::move(path));
       return;
     case CSSPropertyID::kClipPath:
-      style.SetClipPath(ShapeClipPathOperation::Create(std::move(path)));
+      builder.SetClipPath(ShapeClipPathOperation::Create(std::move(path)));
       return;
     default:
       NOTREACHED();
@@ -65,7 +66,7 @@
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
-  SetPath(CssProperty(), *state.Style(),
+  SetPath(CssProperty(), *state.Style(), state.StyleBuilder(),
           PathInterpolationFunctions::AppliedValue(interpolable_value,
                                                    non_interpolable_value));
 }
diff --git a/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
index a424d04..dbb9090 100644
--- a/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
@@ -145,10 +145,10 @@
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
-  ComputedStyle& style = *state.Style();
-  style.SetTextIndent(To<InterpolableLength>(interpolable_value)
-                          .CreateLength(state.CssToLengthConversionData(),
-                                        Length::ValueRange::kAll));
+  state.StyleBuilder().SetTextIndent(
+      To<InterpolableLength>(interpolable_value)
+          .CreateLength(state.CssToLengthConversionData(),
+                        Length::ValueRange::kAll));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
index 5564da4..9c3237c 100644
--- a/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
@@ -200,7 +200,7 @@
   EVisibility visibility =
       To<CSSVisibilityNonInterpolableValue>(non_interpolable_value)
           ->Visibility(fraction);
-  state.Style()->SetVisibility(visibility);
+  state.StyleBuilder().SetVisibility(visibility);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/length_list_property_functions.cc b/third_party/blink/renderer/core/animation/length_list_property_functions.cc
index b197be8..5574214 100644
--- a/third_party/blink/renderer/core/animation/length_list_property_functions.cc
+++ b/third_party/blink/renderer/core/animation/length_list_property_functions.cc
@@ -214,7 +214,7 @@
       return;
 
     case CSSPropertyID::kObjectPosition:
-      style.SetObjectPosition(PointFromVector(length_list));
+      builder.SetObjectPosition(PointFromVector(length_list));
       return;
     case CSSPropertyID::kOffsetAnchor:
       builder.SetOffsetAnchor(PointFromVector(length_list));
diff --git a/third_party/blink/renderer/core/animation/length_property_functions.cc b/third_party/blink/renderer/core/animation/length_property_functions.cc
index b816b96f..a7c9374a 100644
--- a/third_party/blink/renderer/core/animation/length_property_functions.cc
+++ b/third_party/blink/renderer/core/animation/length_property_functions.cc
@@ -320,7 +320,6 @@
 }
 
 bool LengthPropertyFunctions::SetLength(const CSSProperty& property,
-                                        ComputedStyle& style,
                                         ComputedStyleBuilder& builder,
                                         const Length& value) {
   switch (property.PropertyID()) {
@@ -342,7 +341,7 @@
       builder.SetFlexBasis(value);
       return true;
     case CSSPropertyID::kHeight:
-      style.SetHeight(value);
+      builder.SetHeight(value);
       return true;
     case CSSPropertyID::kLeft:
       builder.SetLeft(value);
@@ -399,7 +398,7 @@
       builder.SetRight(value);
       return true;
     case CSSPropertyID::kShapeMargin:
-      style.SetShapeMargin(value);
+      builder.SetShapeMargin(value);
       return true;
     case CSSPropertyID::kStrokeDashoffset:
       builder.SetStrokeDashOffset(value);
@@ -408,7 +407,7 @@
       builder.SetTop(value);
       return true;
     case CSSPropertyID::kWidth:
-      style.SetWidth(value);
+      builder.SetWidth(value);
       return true;
     case CSSPropertyID::kWebkitPerspectiveOriginX:
       builder.SetPerspectiveOriginX(value);
diff --git a/third_party/blink/renderer/core/animation/length_property_functions.h b/third_party/blink/renderer/core/animation/length_property_functions.h
index d343dde..1a2c9b6 100644
--- a/third_party/blink/renderer/core/animation/length_property_functions.h
+++ b/third_party/blink/renderer/core/animation/length_property_functions.h
@@ -32,7 +32,6 @@
                         const ComputedStyle&,
                         Length& result);
   static bool SetLength(const CSSProperty&,
-                        ComputedStyle&,
                         ComputedStyleBuilder&,
                         const Length&);
 };
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc b/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
index b66a738..a969226 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc
@@ -107,12 +107,13 @@
                                        WritingMode::kVerticalRl};
   Vector<TextDirection> directions = {TextDirection::kLtr, TextDirection::kRtl};
 
-  scoped_refptr<ComputedStyle> style =
-      GetDocument().GetStyleResolver().CreateComputedStyle();
   for (const WritingMode& writing_mode : writing_modes) {
     for (const TextDirection& direction : directions) {
-      style->SetWritingMode(writing_mode);
-      style->SetDirection(direction);
+      ComputedStyleBuilder style_builder =
+          GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+      style_builder.SetWritingMode(writing_mode);
+      style_builder.SetDirection(direction);
+      scoped_refptr<const ComputedStyle> style = style_builder.TakeStyle();
       EXPECT_EQ(ConvertOrientation(ScrollTimeline::ScrollDirection::kVertical,
                                    style.get()),
                 CompositorScrollTimeline::ScrollDown);
@@ -124,12 +125,12 @@
 }
 
 TEST_F(ScrollTimelineUtilTest, ConvertOrientationLogical) {
-  scoped_refptr<ComputedStyle> style =
-      GetDocument().GetStyleResolver().CreateComputedStyle();
-
   // horizontal-tb, ltr
-  style->SetWritingMode(WritingMode::kHorizontalTb);
-  style->SetDirection(TextDirection::kLtr);
+  ComputedStyleBuilder builder =
+      GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.SetWritingMode(WritingMode::kHorizontalTb);
+  builder.SetDirection(TextDirection::kLtr);
+  scoped_refptr<const ComputedStyle> style = builder.TakeStyle();
   EXPECT_EQ(
       ConvertOrientation(ScrollTimeline::ScrollDirection::kBlock, style.get()),
       CompositorScrollTimeline::ScrollDown);
@@ -138,8 +139,10 @@
       CompositorScrollTimeline::ScrollRight);
 
   // vertical-lr, ltr
-  style->SetWritingMode(WritingMode::kVerticalLr);
-  style->SetDirection(TextDirection::kLtr);
+  builder = GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.SetWritingMode(WritingMode::kVerticalLr);
+  builder.SetDirection(TextDirection::kLtr);
+  style = builder.TakeStyle();
   EXPECT_EQ(
       ConvertOrientation(ScrollTimeline::ScrollDirection::kBlock, style.get()),
       CompositorScrollTimeline::ScrollRight);
@@ -148,8 +151,10 @@
       CompositorScrollTimeline::ScrollDown);
 
   // vertical-rl, ltr
-  style->SetWritingMode(WritingMode::kVerticalRl);
-  style->SetDirection(TextDirection::kLtr);
+  builder = GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.SetWritingMode(WritingMode::kVerticalRl);
+  builder.SetDirection(TextDirection::kLtr);
+  style = builder.TakeStyle();
   EXPECT_EQ(
       ConvertOrientation(ScrollTimeline::ScrollDirection::kBlock, style.get()),
       CompositorScrollTimeline::ScrollLeft);
@@ -158,8 +163,10 @@
       CompositorScrollTimeline::ScrollDown);
 
   // horizontal-tb, rtl
-  style->SetWritingMode(WritingMode::kHorizontalTb);
-  style->SetDirection(TextDirection::kRtl);
+  builder = GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.SetWritingMode(WritingMode::kHorizontalTb);
+  builder.SetDirection(TextDirection::kRtl);
+  style = builder.TakeStyle();
   EXPECT_EQ(
       ConvertOrientation(ScrollTimeline::ScrollDirection::kBlock, style.get()),
       CompositorScrollTimeline::ScrollDown);
@@ -168,8 +175,10 @@
       CompositorScrollTimeline::ScrollLeft);
 
   // vertical-lr, rtl
-  style->SetWritingMode(WritingMode::kVerticalLr);
-  style->SetDirection(TextDirection::kRtl);
+  builder = GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.SetWritingMode(WritingMode::kVerticalLr);
+  builder.SetDirection(TextDirection::kRtl);
+  style = builder.TakeStyle();
   EXPECT_EQ(
       ConvertOrientation(ScrollTimeline::ScrollDirection::kBlock, style.get()),
       CompositorScrollTimeline::ScrollRight);
@@ -178,8 +187,10 @@
       CompositorScrollTimeline::ScrollUp);
 
   // vertical-rl, rtl
-  style->SetWritingMode(WritingMode::kVerticalRl);
-  style->SetDirection(TextDirection::kRtl);
+  builder = GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.SetWritingMode(WritingMode::kVerticalRl);
+  builder.SetDirection(TextDirection::kRtl);
+  style = builder.TakeStyle();
   EXPECT_EQ(
       ConvertOrientation(ScrollTimeline::ScrollDirection::kBlock, style.get()),
       CompositorScrollTimeline::ScrollLeft);
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
index 9a6b306..98f2b71 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
@@ -64,10 +64,10 @@
             double height,
             unsigned container_type,
             PhysicalAxes contained_axes) {
-    scoped_refptr<ComputedStyle> style =
-        GetDocument().GetStyleResolver().InitialStyleForElement();
-    style->SetContainerType(container_type);
-    ContainerElement().SetComputedStyle(style);
+    ComputedStyleBuilder builder(
+        *GetDocument().GetStyleResolver().InitialStyleForElement());
+    builder.SetContainerType(container_type);
+    ContainerElement().SetComputedStyle(builder.TakeStyle());
 
     ContainerQuery* container_query = ParseContainer(query);
     DCHECK(container_query);
@@ -113,10 +113,10 @@
                               PhysicalSize size,
                               unsigned container_type,
                               PhysicalAxes axes) {
-    scoped_refptr<ComputedStyle> style =
-        GetDocument().GetStyleResolver().InitialStyleForElement();
-    style->SetContainerType(container_type);
-    ContainerElement().SetComputedStyle(style);
+    ComputedStyleBuilder builder(
+        *GetDocument().GetStyleResolver().InitialStyleForElement());
+    builder.SetContainerType(container_type);
+    ContainerElement().SetComputedStyle(builder.TakeStyle());
     return evaluator->SizeContainerChanged(GetDocument(), ContainerElement(),
                                            size, axes);
   }
@@ -285,9 +285,10 @@
   PhysicalSize size_100(LayoutUnit(100), LayoutUnit(100));
 
   Element& container_element = ContainerElement();
-  scoped_refptr<ComputedStyle> style =
-      GetDocument().GetStyleResolver().InitialStyleForElement();
-  style->SetContainerType(type_inline_size);
+  ComputedStyleBuilder builder(
+      *GetDocument().GetStyleResolver().InitialStyleForElement());
+  builder.SetContainerType(type_inline_size);
+  scoped_refptr<ComputedStyle> style = builder.TakeStyle();
   container_element.SetComputedStyle(style);
 
   auto* evaluator = MakeGarbageCollected<ContainerQueryEvaluator>();
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 9855af6..2584cd5 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -960,6 +960,7 @@
       valid_for_marker: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "font-family",
@@ -1322,6 +1323,7 @@
       style_builder_custom_functions: ["value"],
       type_name: "short",
       typedom_types: ["Number"],
+      readonly: true,
     },
     {
       name: "text-orientation",
@@ -1337,6 +1339,7 @@
       priority: "High",
       computable: false,
       valid_for_formatted_text: true,
+      readonly: true,
     },
     {
       name: "-webkit-text-orientation",
@@ -1359,6 +1362,7 @@
       style_builder_custom_functions: ["initial", "inherit", "value"],
       priority: "High",
       valid_for_formatted_text: true,
+      readonly: true,
     },
     {
       name: "-webkit-writing-mode",
@@ -1390,6 +1394,7 @@
       // Setting zoom affects the _EffectiveZoom_, which in turns affects every px value
       // stored on ComputedStyle; see CSSToLengthConversionData::ZoomedComputedPixels.
       supports_incremental_style: false,
+      readonly: true,
     },
     {
       name: "accent-color",
@@ -1467,6 +1472,7 @@
       keywords: ["none"],
       typedom_types: ["Keyword"],
       runtime_flag: "CSSAnchorPositioning",
+      readonly: true,
     },
     {
       name: "anchor-scroll",
@@ -1479,6 +1485,7 @@
       keywords: ["none"],
       typedom_types: ["Keyword"],
       runtime_flag: "CSSAnchorPositioning",
+      readonly: true,
     },
     {
       name: "aspect-ratio",
@@ -1492,6 +1499,7 @@
       converter: "ConvertAspectRatio",
       include_paths: ["third_party/blink/renderer/core/style/style_aspect_ratio.h"],
       computable: false,
+      readonly: true,
     },
     {
       name: "backdrop-filter",
@@ -1508,6 +1516,7 @@
       style_builder_custom_functions: ["value"],
       keywords: ["none"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "backface-visibility",
@@ -2175,6 +2184,7 @@
       typedom_types: ["Keyword"],
       valid_for_first_letter: true,
       valid_for_first_line: true,
+      readonly: true,
     },
     {
       name: "box-sizing",
@@ -2184,6 +2194,7 @@
       keywords: ["content-box", "border-box"],
       typedom_types: ["Keyword"],
       default_value: "content-box",
+      readonly: true,
     },
     {
       name: "break-after",
@@ -2198,6 +2209,7 @@
       typedom_types: ["Keyword"],
       default_value: "auto",
       type_name: "EBreakBetween",
+      readonly: true,
     },
     {
       name: "break-before",
@@ -2212,6 +2224,7 @@
       typedom_types: ["Keyword"],
       default_value: "auto",
       type_name: "EBreakBetween",
+      readonly: true,
     },
     {
       name: "break-inside",
@@ -2222,6 +2235,7 @@
       keywords: ["auto", "avoid", "avoid-column", "avoid-page"],
       typedom_types: ["Keyword"],
       default_value: "auto",
+      readonly: true,
     },
     {
       name: "buffered-rendering",
@@ -2241,6 +2255,7 @@
       keywords: ["top", "bottom"],
       typedom_types: ["Keyword"],
       default_value: "top",
+      readonly: true,
     },
     {
       name: "caret-color",
@@ -2267,6 +2282,7 @@
       keywords: ["none", "left", "right", "both", "inline-start", "inline-end"],
       typedom_types: ["Keyword"],
       default_value: "none",
+      readonly: true,
     },
     {
       name: "clip",
@@ -2282,6 +2298,7 @@
       converter: "ConvertClip",
       keywords: ["auto"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "clip-path",
@@ -2298,6 +2315,7 @@
       converter: "ConvertClipPath",
       keywords: ["none"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "clip-rule",
@@ -2373,6 +2391,7 @@
       getter: "GetColumnFill",
       typedom_types: ["Keyword"],
       computable: false,
+      readonly: true,
     },
     {
       name: "contain",
@@ -2387,6 +2406,7 @@
       keywords: ["none", "strict", "content", "size", "layout", "style", "paint", "inline-size", "block-size"],
       typedom_types: ["Keyword"],
       computable: false,
+      readonly: true,
     },
     {
       name: "contain-intrinsic-width",
@@ -2399,6 +2419,7 @@
       default_value: "absl::nullopt",
       type_name: "absl::optional<StyleIntrinsicLength>",
       converter: "ConvertIntrinsicDimension",
+      readonly: true,
     },
     {
       name: "contain-intrinsic-height",
@@ -2411,6 +2432,7 @@
       default_value: "absl::optional<StyleIntrinsicLength>()",
       type_name: "absl::optional<StyleIntrinsicLength>",
       converter: "ConvertIntrinsicDimension",
+      readonly: true,
     },
     {
       name: "container-name",
@@ -2423,6 +2445,7 @@
       keywords: ["none"],
       typedom_types: ["Keyword"],
       runtime_flag: "CSSContainerQueries",
+      readonly: true,
     },
     {
       name: "container-type",
@@ -2436,6 +2459,7 @@
       converter: "ConvertFlags<EContainerType, CSSValueID::kNormal>",
       typedom_types: ["Keyword"],
       runtime_flag: "CSSContainerQueries",
+      readonly: true,
     },
     {
       name: "content",
@@ -2447,11 +2471,12 @@
       default_value: "nullptr",
       separator: ",",
       type_name: "ContentData",
-      computed_style_custom_functions: ["getter", "setter"],
+      computed_style_custom_functions: ["getter"],
       style_builder_custom_functions: ["initial", "inherit", "value"],
       valid_for_marker: true,
       tree_scoped_value: true,
       supports_incremental_style: true,
+      readonly: true,
     },
     {
       name: "counter-increment",
@@ -2504,6 +2529,7 @@
       style_builder_custom_functions: ["initial", "inherit", "value"],
       typedom_types: ["Keyword"],
       valid_for_highlight_legacy: true,
+      readonly: true,
     },
     {
       name: "cx",
@@ -2561,6 +2587,7 @@
       // (e.g. OriginalDisplay is based on display, and setting float can cause blockification),
       // so we turn off incremental style for all them all.
       supports_incremental_style: false,
+      readonly: true,
     },
     {
       name: "dominant-baseline",
@@ -2583,6 +2610,7 @@
       keywords: ["show", "hide"],
       typedom_types: ["Keyword"],
       default_value: "show",
+      readonly: true,
     },
     {
       name: "fill",
@@ -2647,6 +2675,7 @@
       style_builder_custom_functions: ["value"],
       keywords: ["none"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "flex-basis",
@@ -2715,6 +2744,7 @@
       valid_for_first_letter: true,
       // See comment on display.
       supports_incremental_style: false,
+      readonly: true,
     },
     {
       name: "flood-color",
@@ -2903,6 +2933,7 @@
       supports_incremental_style: true,
       valid_for_formatted_text: true,
       valid_for_position_fallback: true,
+      readonly: true,
     },
     {
       name: "hyphenate-limit-chars",
@@ -2916,6 +2947,7 @@
       include_paths: ["third_party/blink/renderer/core/style/style_hyphenate_limit_chars.h"],
       converter: "ConvertHyphenateLimitChars",
       runtime_flag: "CSSHyphenateLimitChars",
+      readonly: true,
     },
     {
       name: "hyphens",
@@ -2928,6 +2960,7 @@
       type_name: "Hyphens",
       typedom_types: ["Keyword"],
       valid_for_marker: true,
+      readonly: true,
     },
     {
       name: "image-rendering",
@@ -2941,6 +2974,7 @@
       ],
       typedom_types: ["Keyword"],
       default_value: "auto",
+      readonly: true,
     },
     {
       name: "image-orientation",
@@ -2952,6 +2986,7 @@
       name_for_methods: "RespectImageOrientation",
       type_name: "bool",
       converter: "ConvertImageOrientation",
+      readonly: true,
     },
     {
       name: "initial-letter",
@@ -2966,6 +3001,7 @@
       runtime_flag: "CSSInitialLetter",
       type_name: "StyleInitialLetter",
       valid_for_first_letter: true,
+      readonly: true,
     },
     {
       name: "isolation",
@@ -2975,6 +3011,7 @@
       keywords: ["auto", "isolate"],
       typedom_types: ["Keyword"],
       default_value: "auto",
+      readonly: true,
     },
     {
       name: "justify-content",
@@ -3091,6 +3128,7 @@
       type_name: "uint8_t",
       converter: "ConvertComputedLength<uint8_t>",
       typedom_types: ["Length"],
+      readonly: true,
     },
     {
       name: "list-style-image",
@@ -3104,9 +3142,9 @@
       default_value: "nullptr",
       typedom_types: ["Keyword", "Image"],
       type_name: "StyleImage",
-      computed_style_custom_functions: ["getter", "setter"],
       style_builder_custom_functions: ["value"],
-      keywords: ["none"]
+      keywords: ["none"],
+      readonly: true,
     },
     {
       name: "list-style-position",
@@ -3117,6 +3155,7 @@
       keywords: ["outside", "inside"],
       typedom_types: ["Keyword"],
       default_value: "outside",
+      readonly: true,
     },
     {
       name: "list-style-type",
@@ -3132,8 +3171,9 @@
         "disc", "circle", "square", "disclosure-open", "disclosure-closed",
         "decimal", "none"
       ],
-      style_builder_custom_functions: ["initial", "inherit", "value"],
+      style_builder_custom_functions: ["value"],
       tree_scoped_value: true,
+      readonly: true,
     },
     {
       name: "margin-bottom",
@@ -3293,7 +3333,8 @@
       keywords: ["normal", "compact"],
       typedom_types: ["Keyword"],
       default_value: "normal",
-      runtime_flag: "CSSMathShift"
+      runtime_flag: "CSSMathShift",
+      readonly: true,
     },
     {
       name: "math-style",
@@ -3304,7 +3345,8 @@
       keywords: ["normal", "compact"],
       typedom_types: ["Keyword"],
       default_value: "normal",
-      runtime_flag: "CSSMathStyle"
+      runtime_flag: "CSSMathStyle",
+      readonly: true,
     },
     {
       name: "max-height",
@@ -3391,6 +3433,7 @@
       default_value: "normal",
       name_for_methods: "BlendMode",
       type_name: "BlendMode",
+      readonly: true,
     },
     {
       name: "object-fit",
@@ -3401,6 +3444,7 @@
       typedom_types: ["Keyword"],
       default_value: "fill",
       getter: "GetObjectFit",
+      readonly: true,
     },
     {
       name: "object-position",
@@ -3413,6 +3457,7 @@
       type_name: "LengthPoint",
       converter: "ConvertPosition",
       typedom_types: ["Keyword", "Position"],
+      readonly: true,
     },
     {
       name: "object-view-box",
@@ -3428,6 +3473,7 @@
       keywords: ["none"],
       typedom_types: ["Keyword"],
       runtime_flag: "CSSObjectViewBox",
+      readonly: true,
     },
     {
       name: "offset-anchor",
@@ -3846,6 +3892,7 @@
       keywords: ["auto"],
       typedom_types: ["Keyword"],
       computable: false,
+      readonly: true,
     },
     {
       name: "page-orientation",
@@ -3857,6 +3904,7 @@
       default_value: "PageOrientation::kUpright",
       include_paths: ["third_party/blink/public/common/css/page_orientation.h"],
       computable: false,
+      readonly: true,
     },
     {
       name: "page-transition-tag",
@@ -3869,6 +3917,7 @@
       keywords: ["none"],
       typedom_types: ["Keyword"],
       runtime_flag: "DocumentTransition",
+      readonly: true,
     },
     {
       name: "paint-order",
@@ -3926,6 +3975,7 @@
       ],
       typedom_types: ["Keyword"],
       default_value: "auto",
+      readonly: true,
     },
     {
       name: "position",
@@ -3941,6 +3991,7 @@
       style_builder_custom_functions: ["inherit"],
       // See comment on display.
       supports_incremental_style: false,
+      readonly: true,
     },
     {
       name: "position-fallback",
@@ -3953,6 +4004,7 @@
       keywords: ["none"],
       typedom_types: ["Keyword"],
       runtime_flag: "CSSAnchorPositioning",
+      readonly: true,
     },
     {
       name: "quotes",
@@ -3968,6 +4020,7 @@
       keywords: ["auto", "none"],
       typedom_types: ["Keyword"],
       computable: false,
+      readonly: true,
     },
     {
       name: "content-visibility",
@@ -3981,6 +4034,7 @@
       // but that value is not unset if the inline style is set incrementally
       // back to content-visibility: visible.
       supports_incremental_style: false,
+      readonly: true,
     },
     {
       name: "resize",
@@ -3992,6 +4046,7 @@
       keywords: ["none", "both", "horizontal", "vertical", "block", "inline"],
       typedom_types: ["Keyword"],
       default_value: "none",
+      readonly: true,
     },
     {
       name: "right",
@@ -4064,6 +4119,7 @@
         "auto", "stable", "both-edges"
       ],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "scrollbar-width",
@@ -4075,6 +4131,7 @@
       default_value: "auto",
       typedom_types: ["Keyword"],
       runtime_flag: "CSSScrollbars",
+      readonly: true,
     },
     {
       name: "scroll-behavior",
@@ -4404,6 +4461,7 @@
       converter: "ConvertLength",
       keywords: ["none"],
       typedom_types: ["Length", "Percentage"],
+      readonly: true,
     },
     {
       name: "shape-outside",
@@ -4418,7 +4476,8 @@
       type_name: "ShapeValue",
       computed_style_custom_functions: ["getter"],
       converter: "ConvertShapeValue",
-      keywords: ["none"]
+      keywords: ["none"],
+      readonly: true,
     },
     {
       name: "shape-rendering",
@@ -4448,6 +4507,7 @@
         "no-punctuation"
       ],
       default_value: "normal",
+      readonly: true,
     },
     {
       name: "stop-color",
@@ -4610,6 +4670,7 @@
       ],
       typedom_types: ["Keyword"],
       default_value: "auto",
+      readonly: true,
     },
     {
       name: "tab-size",
@@ -4643,6 +4704,7 @@
       getter: "GetTextAlign",
       style_builder_custom_functions: ["value"],
       valid_for_formatted_text: true,
+      readonly: true,
     },
     {
       name: "text-align-last",
@@ -4653,6 +4715,7 @@
       keywords: ["auto", "start", "end", "left", "right", "center", "justify"],
       default_value: "auto",
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "text-anchor",
@@ -4678,6 +4741,7 @@
       valid_for_marker: true,
       computable: false,
       valid_for_formatted_text: true,
+      readonly: true,
     },
     {
       name: "text-decoration-color",
@@ -4720,6 +4784,7 @@
       valid_for_highlight: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-decoration-skip-ink",
@@ -4738,6 +4803,7 @@
       valid_for_highlight: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-decoration-style",
@@ -4754,6 +4820,7 @@
       valid_for_highlight: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-decoration-thickness",
@@ -4774,6 +4841,7 @@
       computable: false,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-indent",
@@ -4783,8 +4851,9 @@
       field_group: "*",
       field_template: "<length>",
       default_value: "Length::Fixed()",
-      style_builder_custom_functions: ["initial", "inherit", "value"],
+      style_builder_custom_functions: ["value"],
       typedom_types: ["Length", "Percentage"],
+      readonly: true,
     },
     {
       name: "text-overflow",
@@ -4819,6 +4888,7 @@
       valid_for_highlight: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-size-adjust",
@@ -4850,6 +4920,7 @@
       valid_for_marker: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-underline-offset",
@@ -4867,6 +4938,7 @@
       computable: false,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "text-underline-position",
@@ -4885,6 +4957,7 @@
       valid_for_first_line: true,
       valid_for_formatted_text: true,
       valid_for_formatted_text_run: true,
+      readonly: true,
     },
     {
       name: "toggle-group",
@@ -4901,6 +4974,7 @@
       supports_incremental_style: true,
       keywords: ["none"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "toggle-root",
@@ -4917,6 +4991,7 @@
       supports_incremental_style: true,
       keywords: ["none"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "toggle-trigger",
@@ -4933,6 +5008,7 @@
       supports_incremental_style: true,
       keywords: ["none"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "toggle-visibility",
@@ -4946,6 +5022,7 @@
       supports_incremental_style: true,
       keywords: ["normal"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "top",
@@ -4978,6 +5055,7 @@
       converter: "ConvertFlags<blink::TouchAction>",
       keywords: ["auto", "none", "pan-x", "pan-left", "pan-right", "pan-y", "pan-up", "pan-down", "pinch-zoom", "manipulation"],
       typedom_types: ["Keyword"],
+      readonly: true,
     },
     {
       name: "transform",
@@ -5099,6 +5177,7 @@
       default_value: "normal",
       type_name: "UnicodeBidi",
       valid_for_marker: true,
+      readonly: true,
     },
     {
       name: "vector-effect",
@@ -5170,6 +5249,7 @@
       valid_for_first_letter: true,
       valid_for_first_line: true,
       valid_for_cue: true,
+      readonly: true,
     },
     {
       name: "x",
@@ -5205,6 +5285,7 @@
       computed_style_custom_functions: ["getter"],
       default_value: "kNoControlPart",
       type_name: "ControlPart",
+      readonly: true,
     },
     {
       name: "-webkit-appearance",
@@ -5219,6 +5300,7 @@
       default_value: "none",
       name_for_methods: "DraggableRegionMode",
       style_builder_custom_functions: ["initial", "inherit", "value"],
+      readonly: true,
     },
     {
       name: "-webkit-app-region",
@@ -5772,7 +5854,8 @@
       converter: "ConvertStyleColor",
       style_builder_template: "color",
       valid_for_highlight_legacy: true,
-      valid_for_highlight: true
+      valid_for_highlight: true,
+      readonly: true,
     },
     {
       name: "-webkit-text-security",
@@ -5892,6 +5975,7 @@
       default_value: "normal",
       valid_for_cue: true,
       valid_for_marker: true,
+      readonly: true,
     },
     {
       name: "widows",
@@ -5924,6 +6008,7 @@
       supports_incremental_style: true,
       valid_for_formatted_text: true,
       valid_for_position_fallback: true,
+      readonly: true,
     },
     {
       name: "will-change",
@@ -5943,6 +6028,7 @@
       default_value: "normal",
       typedom_types: ["Keyword"],
       valid_for_marker: true,
+      readonly: true,
     },
     {
       name: "word-spacing",
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 6490ebc..15b80c0 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -548,7 +548,7 @@
 
 void BackdropFilter::ApplyValue(StyleResolverState& state,
                                 const CSSValue& value) const {
-  state.Style()->SetBackdropFilter(
+  state.StyleBuilder().SetBackdropFilter(
       StyleBuilderConverter::ConvertFilterOperations(state, value,
                                                      PropertyID()));
 }
@@ -2211,7 +2211,7 @@
 }
 
 void Content::ApplyInitial(StyleResolverState& state) const {
-  state.Style()->SetContent(nullptr);
+  state.StyleBuilder().SetContent(nullptr);
 }
 
 void Content::ApplyInherit(StyleResolverState& state) const {
@@ -2226,14 +2226,15 @@
 
 void Content::ApplyValue(StyleResolverState& state,
                          const ScopedCSSValue& scoped_value) const {
+  ComputedStyleBuilder& builder = state.StyleBuilder();
   const CSSValue& value = scoped_value.GetCSSValue();
   if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
     DCHECK(identifier_value->GetValueID() == CSSValueID::kNormal ||
            identifier_value->GetValueID() == CSSValueID::kNone);
     if (identifier_value->GetValueID() == CSSValueID::kNone)
-      state.Style()->SetContent(MakeGarbageCollected<NoneContentData>());
+      builder.SetContent(MakeGarbageCollected<NoneContentData>());
     else
-      state.Style()->SetContent(nullptr);
+      builder.SetContent(nullptr);
     return;
   }
   const CSSValueList& outer_list = To<CSSValueList>(value);
@@ -2311,7 +2312,7 @@
     prev_content->SetNext(alt_content);
   }
   DCHECK(first_content);
-  state.Style()->SetContent(first_content);
+  builder.SetContent(first_content);
 }
 
 const int kCounterIncrementDefaultValue = 1;
@@ -2451,35 +2452,35 @@
 }
 
 void Cursor::ApplyInitial(StyleResolverState& state) const {
-  state.Style()->ClearCursorList();
-  state.Style()->SetCursor(ComputedStyleInitialValues::InitialCursor());
+  ComputedStyleBuilder& builder = state.StyleBuilder();
+  builder.ClearCursorList();
+  builder.SetCursor(ComputedStyleInitialValues::InitialCursor());
 }
 
 void Cursor::ApplyInherit(StyleResolverState& state) const {
-  state.Style()->SetCursor(state.ParentStyle()->Cursor());
-  state.Style()->SetCursorList(state.ParentStyle()->Cursors());
+  ComputedStyleBuilder& builder = state.StyleBuilder();
+  builder.SetCursor(state.ParentStyle()->Cursor());
+  builder.SetCursorList(state.ParentStyle()->Cursors());
 }
 
 void Cursor::ApplyValue(StyleResolverState& state,
                         const CSSValue& value) const {
-  state.Style()->ClearCursorList();
+  ComputedStyleBuilder& builder = state.StyleBuilder();
+  builder.ClearCursorList();
   if (auto* value_list = DynamicTo<CSSValueList>(value)) {
-    state.Style()->SetCursor(ECursor::kAuto);
+    builder.SetCursor(ECursor::kAuto);
     for (const auto& item : *value_list) {
       if (const auto* cursor =
               DynamicTo<cssvalue::CSSCursorImageValue>(*item)) {
         const CSSValue& image = cursor->ImageValue();
-        state.Style()->AddCursor(
-            state.GetStyleImage(CSSPropertyID::kCursor, image),
-            cursor->HotSpotSpecified(), cursor->HotSpot());
+        builder.AddCursor(state.GetStyleImage(CSSPropertyID::kCursor, image),
+                          cursor->HotSpotSpecified(), cursor->HotSpot());
       } else {
-        state.Style()->SetCursor(
-            To<CSSIdentifierValue>(*item).ConvertTo<ECursor>());
+        builder.SetCursor(To<CSSIdentifierValue>(*item).ConvertTo<ECursor>());
       }
     }
   } else {
-    state.Style()->SetCursor(
-        To<CSSIdentifierValue>(value).ConvertTo<ECursor>());
+    builder.SetCursor(To<CSSIdentifierValue>(value).ConvertTo<ECursor>());
   }
 }
 
@@ -2535,7 +2536,7 @@
 
 void Direction::ApplyValue(StyleResolverState& state,
                            const CSSValue& value) const {
-  state.Style()->SetDirection(
+  state.StyleBuilder().SetDirection(
       To<CSSIdentifierValue>(value).ConvertTo<TextDirection>());
 }
 
@@ -2776,7 +2777,7 @@
 
 void Filter::ApplyValue(StyleResolverState& state,
                         const CSSValue& value) const {
-  state.Style()->SetFilter(StyleBuilderConverter::ConvertFilterOperations(
+  state.StyleBuilder().SetFilter(StyleBuilderConverter::ConvertFilterOperations(
       state, value, PropertyID()));
 }
 
@@ -4597,7 +4598,7 @@
 
 void ListStyleImage::ApplyValue(StyleResolverState& state,
                                 const CSSValue& value) const {
-  state.Style()->SetListStyleImage(
+  state.StyleBuilder().SetListStyleImage(
       state.GetStyleImage(CSSPropertyID::kListStyleImage, value));
 }
 
@@ -4637,15 +4638,6 @@
       style.ListStyleType()->GetCounterStyleName());
 }
 
-void ListStyleType::ApplyInitial(StyleResolverState& state) const {
-  state.Style()->SetListStyleType(
-      ComputedStyleInitialValues::InitialListStyleType());
-}
-
-void ListStyleType::ApplyInherit(StyleResolverState& state) const {
-  state.Style()->SetListStyleType(state.ParentStyle()->ListStyleType());
-}
-
 void ListStyleType::ApplyValue(StyleResolverState& state,
                                const CSSValue& value) const {
   NOTREACHED();
@@ -4653,22 +4645,23 @@
 
 void ListStyleType::ApplyValue(StyleResolverState& state,
                                const ScopedCSSValue& scoped_value) const {
+  ComputedStyleBuilder& builder = state.StyleBuilder();
   const CSSValue& value = scoped_value.GetCSSValue();
   if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
     DCHECK_EQ(CSSValueID::kNone, identifier_value->GetValueID());
-    state.Style()->SetListStyleType(nullptr);
+    builder.SetListStyleType(nullptr);
     return;
   }
 
   if (const auto* string_value = DynamicTo<CSSStringValue>(value)) {
-    state.Style()->SetListStyleType(
+    builder.SetListStyleType(
         ListStyleTypeData::CreateString(AtomicString(string_value->Value())));
     return;
   }
 
   DCHECK(value.IsCustomIdentValue());
   const auto& custom_ident_value = To<CSSCustomIdentValue>(value);
-  state.Style()->SetListStyleType(ListStyleTypeData::CreateCounterStyle(
+  builder.SetListStyleType(ListStyleTypeData::CreateCounterStyle(
       custom_ident_value.Value(), scoped_value.GetTreeScope()));
 }
 
@@ -4946,20 +4939,21 @@
 
 void MathDepth::ApplyValue(StyleResolverState& state,
                            const CSSValue& value) const {
+  ComputedStyleBuilder& builder = state.StyleBuilder();
   if (const auto* list = DynamicTo<CSSValueList>(value)) {
     DCHECK_EQ(list->length(), 1U);
     const auto& relative_value = To<CSSPrimitiveValue>(list->Item(0));
-    state.Style()->SetMathDepth(base::ClampAdd(state.ParentStyle()->MathDepth(),
-                                               relative_value.GetIntValue()));
+    builder.SetMathDepth(base::ClampAdd(state.ParentStyle()->MathDepth(),
+                                        relative_value.GetIntValue()));
   } else if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
     DCHECK(identifier_value->GetValueID() == CSSValueID::kAutoAdd);
     int16_t depth = 0;
     if (state.ParentStyle()->MathStyle() == EMathStyle::kCompact)
       depth += 1;
-    state.Style()->SetMathDepth(
+    builder.SetMathDepth(
         base::ClampAdd(state.ParentStyle()->MathDepth(), depth));
   } else if (DynamicTo<CSSPrimitiveValue>(value)) {
-    state.Style()->SetMathDepth(
+    builder.SetMathDepth(
         ClampTo<int16_t>(To<CSSPrimitiveValue>(value).GetIntValue()));
   }
 }
@@ -5954,7 +5948,7 @@
 
 void Position::ApplyInherit(StyleResolverState& state) const {
   if (!state.ParentNode()->IsDocumentNode())
-    state.Style()->SetPosition(state.ParentStyle()->GetPosition());
+    state.StyleBuilder().SetPosition(state.ParentStyle()->GetPosition());
 }
 
 const CSSValue* PositionFallback::ParseSingleValue(
@@ -6050,7 +6044,7 @@
   } else {
     r = identifier_value.ConvertTo<EResize>();
   }
-  state.Style()->SetResize(r);
+  state.StyleBuilder().SetResize(r);
 }
 
 const CSSValue* Right::ParseSingleValue(
@@ -7103,6 +7097,7 @@
 
 void TextAlign::ApplyValue(StyleResolverState& state,
                            const CSSValue& value) const {
+  ComputedStyleBuilder& builder = state.StyleBuilder();
   const auto* ident_value = DynamicTo<CSSIdentifierValue>(value);
   if (ident_value &&
       ident_value->GetValueID() != CSSValueID::kWebkitMatchParent) {
@@ -7112,19 +7107,19 @@
     if (ident_value->GetValueID() == CSSValueID::kInternalCenter &&
         state.ParentStyle()->GetTextAlign() !=
             ComputedStyleInitialValues::InitialTextAlign())
-      state.Style()->SetTextAlign(state.ParentStyle()->GetTextAlign());
+      builder.SetTextAlign(state.ParentStyle()->GetTextAlign());
     else
-      state.Style()->SetTextAlign(ident_value->ConvertTo<ETextAlign>());
+      builder.SetTextAlign(ident_value->ConvertTo<ETextAlign>());
   } else if (state.ParentStyle()->GetTextAlign() == ETextAlign::kStart) {
-    state.Style()->SetTextAlign(state.ParentStyle()->IsLeftToRightDirection()
-                                    ? ETextAlign::kLeft
-                                    : ETextAlign::kRight);
+    builder.SetTextAlign(state.ParentStyle()->IsLeftToRightDirection()
+                             ? ETextAlign::kLeft
+                             : ETextAlign::kRight);
   } else if (state.ParentStyle()->GetTextAlign() == ETextAlign::kEnd) {
-    state.Style()->SetTextAlign(state.ParentStyle()->IsLeftToRightDirection()
-                                    ? ETextAlign::kRight
-                                    : ETextAlign::kLeft);
+    builder.SetTextAlign(state.ParentStyle()->IsLeftToRightDirection()
+                             ? ETextAlign::kRight
+                             : ETextAlign::kLeft);
   } else {
-    state.Style()->SetTextAlign(state.ParentStyle()->GetTextAlign());
+    builder.SetTextAlign(state.ParentStyle()->GetTextAlign());
   }
 }
 
@@ -7270,14 +7265,6 @@
   return list;
 }
 
-void TextIndent::ApplyInitial(StyleResolverState& state) const {
-  state.Style()->SetTextIndent(ComputedStyleInitialValues::InitialTextIndent());
-}
-
-void TextIndent::ApplyInherit(StyleResolverState& state) const {
-  state.Style()->SetTextIndent(state.ParentStyle()->TextIndent());
-}
-
 void TextIndent::ApplyValue(StyleResolverState& state,
                             const CSSValue& value) const {
   Length length_or_percentage_value;
@@ -7292,7 +7279,7 @@
     }
   }
 
-  state.Style()->SetTextIndent(length_or_percentage_value);
+  state.StyleBuilder().SetTextIndent(length_or_percentage_value);
 }
 
 const CSSValue* TextOrientation::CSSValueFromComputedStyleInternal(
@@ -7993,10 +7980,10 @@
 void AppRegion::ApplyValue(StyleResolverState& state,
                            const CSSValue& value) const {
   const auto& identifier_value = To<CSSIdentifierValue>(value);
-  state.Style()->SetDraggableRegionMode(identifier_value.GetValueID() ==
-                                                CSSValueID::kDrag
-                                            ? EDraggableRegionMode::kDrag
-                                            : EDraggableRegionMode::kNoDrag);
+  state.StyleBuilder().SetDraggableRegionMode(
+      identifier_value.GetValueID() == CSSValueID::kDrag
+          ? EDraggableRegionMode::kDrag
+          : EDraggableRegionMode::kNoDrag);
   state.GetDocument().SetHasAnnotatedRegions(true);
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
index bd837a1..ee4010d 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -276,16 +276,17 @@
   }
 }
 
-void ElementStyleResources::LoadPendingSVGResources(ComputedStyle& style) {
+void ElementStyleResources::LoadPendingSVGResources(
+    ComputedStyleBuilder& builder) {
   Document& document = element_.GetDocument();
   for (CSSPropertyID property : pending_svg_resource_properties_) {
     switch (property) {
       case CSSPropertyID::kBackdropFilter:
-        LoadResourcesForFilter(style.MutableBackdropFilter().Operations(),
+        LoadResourcesForFilter(builder.MutableBackdropFilter().Operations(),
                                document);
         break;
       case CSSPropertyID::kFilter:
-        LoadResourcesForFilter(style.MutableFilter().Operations(), document);
+        LoadResourcesForFilter(builder.MutableFilter().Operations(), document);
         break;
       default:
         NOTREACHED();
@@ -299,7 +300,8 @@
   return nullptr;
 }
 
-void ElementStyleResources::LoadPendingImages(ComputedStyle& style) {
+void ElementStyleResources::LoadPendingImages(ComputedStyleBuilder& builder) {
+  ComputedStyle& style = *builder.MutableInternalStyle();
   // We must loop over the properties and then look at the style to see if
   // a pending image exists, and only load that image. For example:
   //
@@ -364,7 +366,7 @@
       }
       case CSSPropertyID::kListStyleImage: {
         if (auto* pending_value = PendingCssValue(style.ListStyleImage()))
-          style.SetListStyleImage(loader.Load(*pending_value));
+          builder.SetListStyleImage(loader.Load(*pending_value));
         break;
       }
       case CSSPropertyID::kBorderImageSource: {
@@ -417,9 +419,9 @@
 }
 
 void ElementStyleResources::LoadPendingResources(
-    ComputedStyle& computed_style) {
-  LoadPendingImages(computed_style);
-  LoadPendingSVGResources(computed_style);
+    ComputedStyleBuilder& builder) {
+  LoadPendingImages(builder);
+  LoadPendingSVGResources(builder);
 }
 
 void ElementStyleResources::UpdateLengthConversionData(
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.h b/third_party/blink/renderer/core/css/resolver/element_style_resources.h
index 9022645..54fbff8 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.h
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.h
@@ -32,7 +32,7 @@
 namespace blink {
 
 class CSSValue;
-class ComputedStyle;
+class ComputedStyleBuilder;
 class Element;
 class PseudoElement;
 class SVGResource;
@@ -77,7 +77,7 @@
   SVGResource* GetSVGResourceFromValue(CSSPropertyID,
                                        const cssvalue::CSSURIValue&);
 
-  void LoadPendingResources(ComputedStyle&);
+  void LoadPendingResources(ComputedStyleBuilder&);
 
   void UpdateLengthConversionData(const CSSToLengthConversionData*);
 
@@ -85,8 +85,8 @@
   bool IsPending(const CSSValue&) const;
   StyleImage* CachedStyleImage(const CSSValue&) const;
 
-  void LoadPendingSVGResources(ComputedStyle&);
-  void LoadPendingImages(ComputedStyle&);
+  void LoadPendingSVGResources(ComputedStyleBuilder&);
+  void LoadPendingImages(ComputedStyleBuilder&);
 
   Element& element_;
   HashSet<CSSPropertyID> pending_image_properties_;
diff --git a/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc b/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc
index 87d4e1d..3e49204 100644
--- a/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/matched_properties_cache_test.cc
@@ -83,7 +83,10 @@
 class MatchedPropertiesCacheTest : public PageTestBase {
  public:
   scoped_refptr<ComputedStyle> CreateStyle() {
-    return GetDocument().GetStyleResolver().InitialStyleForElement();
+    return GetDocument().GetStyleResolver().CreateComputedStyle();
+  }
+  ComputedStyleBuilder CreateStyleBuilder() {
+    return GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
   }
 };
 
@@ -203,12 +206,16 @@
 TEST_F(MatchedPropertiesCacheTest, WritingModeDependency) {
   TestCache cache(GetDocument());
 
-  auto parent_a = CreateStyle();
-  auto parent_b = CreateStyle();
+  auto parent_builder_a = CreateStyleBuilder();
+  parent_builder_a.SetWritingMode(WritingMode::kHorizontalTb);
+  auto parent_builder_b = CreateStyleBuilder();
+  parent_builder_b.SetWritingMode(WritingMode::kVerticalRl);
+
+  auto parent_a = parent_builder_a.TakeStyle();
+  auto parent_b = parent_builder_b.TakeStyle();
+
   auto style_a = CreateStyle();
   auto style_b = CreateStyle();
-  parent_a->SetWritingMode(WritingMode::kHorizontalTb);
-  parent_b->SetWritingMode(WritingMode::kVerticalRl);
 
   TestKey key("display:block", 1, GetDocument());
 
@@ -221,12 +228,16 @@
 TEST_F(MatchedPropertiesCacheTest, DirectionDependency) {
   TestCache cache(GetDocument());
 
-  auto parent_a = CreateStyle();
-  auto parent_b = CreateStyle();
+  auto parent_builder_a = CreateStyleBuilder();
+  parent_builder_a.SetDirection(TextDirection::kLtr);
+  auto parent_builder_b = CreateStyleBuilder();
+  parent_builder_b.SetDirection(TextDirection::kRtl);
+
+  auto parent_a = parent_builder_a.TakeStyle();
+  auto parent_b = parent_builder_b.TakeStyle();
+
   auto style_a = CreateStyle();
   auto style_b = CreateStyle();
-  parent_a->SetDirection(TextDirection::kLtr);
-  parent_b->SetDirection(TextDirection::kRtl);
 
   TestKey key("display:block", 1, GetDocument());
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 9a6ffad..8348da06 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -120,18 +120,19 @@
   return false;
 }
 
-void AdjustStyleForSvgElement(const SVGElement& element, ComputedStyle& style) {
+void AdjustStyleForSvgElement(const SVGElement& element,
+                              ComputedStyleBuilder& builder) {
   // Disable some of text decoration properties.
   //
   // Note that SetFooBar() is more efficient than ResetFooBar() if the current
   // value is same as the reset value.
-  style.SetTextDecorationSkipInk(ETextDecorationSkipInk::kAuto);
-  style.SetTextDecorationStyle(
+  builder.SetTextDecorationSkipInk(ETextDecorationSkipInk::kAuto);
+  builder.SetTextDecorationStyle(
       ETextDecorationStyle::kSolid);  // crbug.com/1246719
-  style.SetTextDecorationThickness(TextDecorationThickness(Length::Auto()));
-  style.SetTextEmphasisMark(TextEmphasisMark::kNone);
-  style.SetTextUnderlineOffset(Length());  // crbug.com/1247912
-  style.SetTextUnderlinePosition(kTextUnderlinePositionAuto);
+  builder.SetTextDecorationThickness(TextDecorationThickness(Length::Auto()));
+  builder.MutableInternalStyle()->SetTextEmphasisMark(TextEmphasisMark::kNone);
+  builder.SetTextUnderlineOffset(Length());  // crbug.com/1247912
+  builder.SetTextUnderlinePosition(kTextUnderlinePositionAuto);
 }
 
 // Adjust style for anchor() and anchor-size() queries.
@@ -308,16 +309,17 @@
   return layout_parent_style.IsDisplayFlexibleOrGridBox();
 }
 
-void StyleAdjuster::AdjustStyleForEditing(ComputedStyle& style) {
+void StyleAdjuster::AdjustStyleForEditing(ComputedStyleBuilder& builder) {
+  const ComputedStyle& style = *builder.InternalStyle();
   if (style.UserModify() != EUserModify::kReadWritePlaintextOnly)
     return;
   // Collapsing whitespace is harmful in plain-text editing.
-  if (style.WhiteSpace() == EWhiteSpace::kNormal)
-    style.SetWhiteSpace(EWhiteSpace::kPreWrap);
-  else if (style.WhiteSpace() == EWhiteSpace::kNowrap)
-    style.SetWhiteSpace(EWhiteSpace::kPre);
-  else if (style.WhiteSpace() == EWhiteSpace::kPreLine)
-    style.SetWhiteSpace(EWhiteSpace::kPreWrap);
+  if (builder.WhiteSpace() == EWhiteSpace::kNormal)
+    builder.SetWhiteSpace(EWhiteSpace::kPreWrap);
+  else if (builder.WhiteSpace() == EWhiteSpace::kNowrap)
+    builder.SetWhiteSpace(EWhiteSpace::kPre);
+  else if (builder.WhiteSpace() == EWhiteSpace::kPreLine)
+    builder.SetWhiteSpace(EWhiteSpace::kPreWrap);
 }
 
 void StyleAdjuster::AdjustStyleForTextCombine(ComputedStyleBuilder& builder) {
@@ -330,32 +332,32 @@
   const auto line_height = style.GetFontHeight().LineHeight();
   const auto size =
       LengthSize(Length::Fixed(line_height), Length::Fixed(one_em));
-  style.SetContainIntrinsicWidth(StyleIntrinsicLength(false, size.Width()));
-  style.SetContainIntrinsicHeight(StyleIntrinsicLength(false, size.Height()));
-  style.SetHeight(size.Height());
+  builder.SetContainIntrinsicWidth(StyleIntrinsicLength(false, size.Width()));
+  builder.SetContainIntrinsicHeight(StyleIntrinsicLength(false, size.Height()));
+  builder.SetHeight(size.Height());
   builder.SetLineHeight(size.Height());
   builder.SetMaxHeight(size.Height());
   builder.SetMaxWidth(size.Width());
   builder.SetMinHeight(size.Height());
   builder.SetMinWidth(size.Width());
-  style.SetWidth(size.Width());
+  builder.SetWidth(size.Width());
   AdjustStyleForCombinedText(builder);
 }
 
 void StyleAdjuster::AdjustStyleForCombinedText(ComputedStyleBuilder& builder) {
   ComputedStyle& style = *builder.MutableInternalStyle();
-  style.ResetTextCombine();
+  builder.ResetTextCombine();
   style.SetLetterSpacing(0.0f);
-  style.SetTextAlign(ETextAlign::kCenter);
-  style.SetTextDecorationLine(TextDecorationLine::kNone);
+  builder.SetTextAlign(ETextAlign::kCenter);
+  builder.SetTextDecorationLine(TextDecorationLine::kNone);
   style.SetTextEmphasisMark(TextEmphasisMark::kNone);
   style.SetVerticalAlign(EVerticalAlign ::kMiddle);
-  style.SetWordBreak(EWordBreak::kKeepAll);
+  builder.SetWordBreak(EWordBreak::kKeepAll);
   style.SetWordSpacing(0.0f);
-  style.SetWritingMode(WritingMode::kHorizontalTb);
+  builder.SetWritingMode(WritingMode::kHorizontalTb);
 
   style.ClearAppliedTextDecorations();
-  style.ResetTextIndent();
+  builder.ResetTextIndent();
   style.UpdateFontOrientation();
 
   DCHECK_EQ(style.GetFont().GetFontDescription().Orientation(),
@@ -400,7 +402,7 @@
     style.SetDisplay(EDisplay::kInlineBlock);
 
     // Do not break inside the marker, and honor the trailing spaces.
-    style.SetWhiteSpace(EWhiteSpace::kPre);
+    builder.SetWhiteSpace(EWhiteSpace::kPre);
 
     // Compute margins for 'outside' during layout, because it requires the
     // layout size of the marker.
@@ -430,7 +432,7 @@
     if (style.GetTextAlign() == ETextAlign::kWebkitLeft ||
         style.GetTextAlign() == ETextAlign::kWebkitCenter ||
         style.GetTextAlign() == ETextAlign::kWebkitRight)
-      style.SetTextAlign(ETextAlign::kStart);
+      builder.SetTextAlign(ETextAlign::kStart);
     return;
   }
 
@@ -438,9 +440,9 @@
     // Frames and framesets never honor position:relative or position:absolute.
     // This is necessary to fix a crash where a site tries to position these
     // objects. They also never honor display nor floating.
-    style.SetPosition(EPosition::kStatic);
-    style.SetDisplay(EDisplay::kBlock);
-    style.SetFloating(EFloat::kNone);
+    builder.SetPosition(EPosition::kStatic);
+    builder.SetDisplay(EDisplay::kBlock);
+    builder.SetFloating(EFloat::kNone);
     return;
   }
 
@@ -483,8 +485,8 @@
   if (IsA<HTMLRTElement>(element)) {
     // Ruby text does not support float or position. This might change with
     // evolution of the specification.
-    style.SetPosition(EPosition::kStatic);
-    style.SetFloating(EFloat::kNone);
+    builder.SetPosition(EPosition::kStatic);
+    builder.SetFloating(EFloat::kNone);
     return;
   }
 
@@ -603,6 +605,7 @@
 }
 
 static void AdjustStyleForDisplay(ComputedStyle& style,
+                                  ComputedStyleBuilder& builder,
                                   const ComputedStyle& layout_parent_style,
                                   const Element* element,
                                   Document* document) {
@@ -638,8 +641,8 @@
       style.Display() == EDisplay::kTableHeaderGroup ||
       style.Display() == EDisplay::kTableRow ||
       style.Display() == EDisplay::kTableRowGroup) {
-    style.SetWritingMode(layout_parent_style.GetWritingMode());
-    style.SetTextOrientation(layout_parent_style.GetTextOrientation());
+    builder.SetWritingMode(layout_parent_style.GetWritingMode());
+    builder.SetTextOrientation(layout_parent_style.GetTextOrientation());
     style.UpdateFontOrientation();
   }
 }
@@ -853,7 +856,7 @@
 
   if (style.Display() != EDisplay::kNone) {
     if (svg_element)
-      AdjustStyleForSvgElement(*svg_element, style);
+      AdjustStyleForSvgElement(*svg_element, builder);
 
     bool is_document_element =
         element && element->GetDocument().documentElement() == element;
@@ -864,7 +867,7 @@
     if (IsInTopLayer(element, style) && !is_document_element) {
       if (style.GetPosition() == EPosition::kStatic ||
           style.GetPosition() == EPosition::kRelative) {
-        style.SetPosition(EPosition::kAbsolute);
+        builder.SetPosition(EPosition::kAbsolute);
       }
       if (style.Display() == EDisplay::kContents) {
         // See crbug.com/1240701 for more details.
@@ -898,7 +901,7 @@
     AdjustStyleForFirstLetter(style);
     AdjustStyleForMarker(style, builder, parent_style, state.GetElement());
 
-    AdjustStyleForDisplay(style, layout_parent_style, element,
+    AdjustStyleForDisplay(style, builder, layout_parent_style, element,
                           element ? &element->GetDocument() : nullptr);
 
     // If this is a child of a LayoutNGCustom, we need the name of the parent
@@ -912,7 +915,7 @@
     // The root element of the main frame has no backdrop, so don't allow
     // it to have a backdrop filter either.
     if (is_document_element && is_in_main_frame && style.HasBackdropFilter())
-      style.MutableBackdropFilter().clear();
+      builder.MutableBackdropFilter().clear();
   } else {
     AdjustStyleForFirstLetter(style);
   }
@@ -974,7 +977,7 @@
 
   AdjustStyleForInert(style, element);
 
-  AdjustStyleForEditing(style);
+  AdjustStyleForEditing(builder);
 
   bool is_svg_root = false;
 
@@ -983,7 +986,7 @@
     if (!is_svg_root) {
       // Only the root <svg> element in an SVG document fragment tree honors css
       // position.
-      style.SetPosition(ComputedStyleInitialValues::InitialPosition());
+      builder.SetPosition(ComputedStyleInitialValues::InitialPosition());
     }
 
     if (style.Display() == EDisplay::kContents &&
@@ -1031,7 +1034,7 @@
     if (style.GetWritingMode() != WritingMode::kHorizontalTb) {
       // TODO(rbuis): this will not work with logical CSS properties.
       // Disable vertical writing-mode for now.
-      style.SetWritingMode(WritingMode::kHorizontalTb);
+      builder.SetWritingMode(WritingMode::kHorizontalTb);
       style.UpdateFontOrientation();
     }
   }
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.h b/third_party/blink/renderer/core/css/resolver/style_adjuster.h
index 34de511..e4d3b77 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.h
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.h
@@ -43,7 +43,7 @@
  public:
   CORE_EXPORT static void AdjustComputedStyle(StyleResolverState&, Element*);
   static void AdjustStyleForCombinedText(ComputedStyleBuilder&);
-  static void AdjustStyleForEditing(ComputedStyle&);
+  static void AdjustStyleForEditing(ComputedStyleBuilder&);
   static void AdjustStyleForTextCombine(ComputedStyleBuilder&);
 
  private:
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_test.cc b/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
index 61547bfb4..b4da625 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
@@ -35,15 +35,15 @@
     for (const CSSValue* value : values) {
       auto parent_style =
           GetDocument().GetStyleResolver().CreateComputedStyle();
-      auto style = GetDocument().GetStyleResolver().CreateComputedStyle();
-      // This test assumes that initial 'writing-mode' is not 'vertical-lr'.
-      ASSERT_NE(WritingMode::kVerticalLr, style->GetWritingMode());
-      style->SetWritingMode(WritingMode::kVerticalLr);
-
       StyleResolverState state(GetDocument(), *GetDocument().body(),
                                nullptr /* StyleRecalcContext */,
                                StyleRequest(parent_style.get()));
-      state.SetStyle(style);
+      state.SetStyle(GetDocument().GetStyleResolver().CreateComputedStyle());
+
+      // This test assumes that initial 'writing-mode' is not 'vertical-lr'.
+      ASSERT_NE(WritingMode::kVerticalLr,
+                state.StyleBuilder().GetWritingMode());
+      state.StyleBuilder().SetWritingMode(WritingMode::kVerticalLr);
 
       ASSERT_FALSE(state.GetFontBuilder().FontDirty());
       StyleBuilder::ApplyProperty(*property, state,
@@ -69,15 +69,15 @@
     for (const CSSValue* value : values) {
       auto parent_style =
           GetDocument().GetStyleResolver().CreateComputedStyle();
-      auto style = GetDocument().GetStyleResolver().CreateComputedStyle();
-      // This test assumes that initial 'text-orientation' is not 'upright'.
-      ASSERT_NE(ETextOrientation::kUpright, style->GetTextOrientation());
-      style->SetTextOrientation(ETextOrientation::kUpright);
-
       StyleResolverState state(GetDocument(), *GetDocument().body(),
                                nullptr /* StyleRecalcContext */,
                                StyleRequest(parent_style.get()));
-      state.SetStyle(style);
+      state.SetStyle(GetDocument().GetStyleResolver().CreateComputedStyle());
+
+      // This test assumes that initial 'text-orientation' is not 'upright'.
+      ASSERT_NE(ETextOrientation::kUpright,
+                state.StyleBuilder().GetTextOrientation());
+      state.StyleBuilder().SetTextOrientation(ETextOrientation::kUpright);
 
       ASSERT_FALSE(state.GetFontBuilder().FontDirty());
       StyleBuilder::ApplyProperty(*property, state,
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
index 4266d9ee..f87d093 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
@@ -438,7 +438,7 @@
   EXPECT_EQ("10px", cascade.ComputedValue("--x"));
   EXPECT_EQ("20px", cascade.ComputedValue("width"));
 
-  cascade.State().StyleRef().SetWidth(Length::Auto());
+  cascade.State().StyleBuilder().SetWidth(Length::Auto());
   cascade.State().StyleRef().SetVariableData("--x", nullptr, true);
   EXPECT_EQ(g_null_atom, cascade.ComputedValue("--x"));
   EXPECT_EQ("auto", cascade.ComputedValue("width"));
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index d882b987..6099df2 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -867,8 +867,7 @@
   builder.SetOverflowX(EOverflow::kAuto);
   builder.SetOverflowY(EOverflow::kAuto);
 
-  GetDocument().GetStyleEngine().ApplyVisionDeficiencyStyle(
-      builder.MutableInternalStyle());
+  GetDocument().GetStyleEngine().ApplyVisionDeficiencyStyle(builder);
 
   return builder.TakeStyle();
 }
@@ -2399,7 +2398,7 @@
       new_viewport_style_builder.SetBackgroundColor(
           StyleColor(background_color));
       new_viewport_style->AccessBackgroundLayers() = background_layers;
-      new_viewport_style->SetImageRendering(image_rendering);
+      new_viewport_style_builder.SetImageRendering(image_rendering);
     }
   }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 24ff3317..c74c6ad7 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -175,7 +175,7 @@
     return;
   }
 
-  element_style_resources_.LoadPendingResources(StyleRef());
+  element_style_resources_.LoadPendingResources(StyleBuilder());
 }
 
 const FontDescription& StyleResolverState::ParentFontDescription() const {
@@ -187,7 +187,7 @@
                                     ? ParentStyle()->EffectiveZoom()
                                     : ComputedStyleInitialValues::InitialZoom();
 
-  StyleRef().SetZoom(f);
+  StyleBuilder().SetZoom(f);
 
   if (f != 1.f)
     GetDocument().CountUse(WebFeature::kCascadedCSSZoomNotEqualToOne);
@@ -202,17 +202,17 @@
 }
 
 void StyleResolverState::SetWritingMode(WritingMode new_writing_mode) {
-  if (StyleRef().GetWritingMode() == new_writing_mode) {
+  if (StyleBuilder().GetWritingMode() == new_writing_mode) {
     return;
   }
-  StyleRef().SetWritingMode(new_writing_mode);
+  StyleBuilder().SetWritingMode(new_writing_mode);
   UpdateLengthConversionData();
   font_builder_.DidChangeWritingMode();
 }
 
 void StyleResolverState::SetTextOrientation(ETextOrientation text_orientation) {
-  if (StyleRef().GetTextOrientation() != text_orientation) {
-    StyleRef().SetTextOrientation(text_orientation);
+  if (StyleBuilder().GetTextOrientation() != text_orientation) {
+    StyleBuilder().SetTextOrientation(text_orientation);
     font_builder_.DidChangeTextOrientation();
   }
 }
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 0a16d0dd..50a7cbee 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -2464,12 +2464,12 @@
 }
 
 void StyleEngine::ApplyVisionDeficiencyStyle(
-    scoped_refptr<ComputedStyle> layout_view_style) {
+    ComputedStyleBuilder& layout_view_style_builder) {
   LoadVisionDeficiencyFilter();
   if (vision_deficiency_filter_) {
     FilterOperations ops;
     ops.Operations().push_back(vision_deficiency_filter_);
-    layout_view_style->SetFilter(ops);
+    layout_view_style_builder.SetFilter(ops);
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 7870052..04edbdb 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -71,6 +71,7 @@
 
 namespace blink {
 
+class ComputedStyleBuilder;
 class CounterStyle;
 class CounterStyleMap;
 class CSSFontSelector;
@@ -493,7 +494,7 @@
 
   void VisionDeficiencyChanged();
   void ApplyVisionDeficiencyStyle(
-      scoped_refptr<ComputedStyle> layout_view_style);
+      ComputedStyleBuilder& layout_view_style_builder);
 
   void CollectMatchingUserRules(ElementRuleCollector&) const;
 
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.h b/third_party/blink/renderer/core/document_transition/document_transition.h
index 32d8849..de4b4341 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/unguessable_token.h"
 #include "third_party/blink/public/common/frame/view_transition_state.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
@@ -26,6 +27,10 @@
 #include "third_party/blink/renderer/platform/heap/forward.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
+namespace viz {
+using NavigationID = base::UnguessableToken;
+}
+
 namespace blink {
 
 class Document;
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_request_forward.h b/third_party/blink/renderer/core/document_transition/document_transition_request_forward.h
index 4c5ba5c..3e998d8 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_request_forward.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition_request_forward.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_FORWARD_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_REQUEST_FORWARD_H_
 
-#include "cc/document_transition/document_transition_request.h"
-
 namespace cc {
 class DocumentTransitionRequest;
 }
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_supplement.h b/third_party/blink/renderer/core/document_transition/document_transition_supplement.h
index 558b20d..eb9cee7 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_supplement.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition_supplement.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_callback.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_request.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index 094d7a6..17cf3706 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -212,13 +212,13 @@
   // For display:contents we should not generate a box, but we generate a non-
   // observable inline box for pseudo elements to be able to locate the
   // anonymous layout objects for generated content during DetachLayoutTree().
-  scoped_refptr<ComputedStyle> layout_style =
-      GetDocument().GetStyleResolver().CreateComputedStyle();
-  layout_style->InheritFrom(style);
-  layout_style->SetContent(style.GetContentData());
-  layout_style->SetDisplay(EDisplay::kInline);
-  layout_style->SetStyleType(pseudo_id_);
-  return layout_style;
+  ComputedStyleBuilder builder =
+      GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+  builder.MutableInternalStyle()->InheritFrom(style);
+  builder.SetContent(style.GetContentData());
+  builder.SetDisplay(EDisplay::kInline);
+  builder.MutableInternalStyle()->SetStyleType(pseudo_id_);
+  return builder.TakeStyle();
 }
 
 void PseudoElement::Dispose() {
diff --git a/third_party/blink/renderer/core/frame/frame_view.cc b/third_party/blink/renderer/core/frame/frame_view.cc
index 6c5f619..fe499af 100644
--- a/third_party/blink/renderer/core/frame/frame_view.cc
+++ b/third_party/blink/renderer/core/frame/frame_view.cc
@@ -131,7 +131,7 @@
         owner_layout_object->PhysicalContentBoxOffset());
     TransformationMatrix matrix =
         parent_frame_to_iframe_content_transform.AccumulatedTransform()
-            .Inverse();
+            .InverseOrIdentity();
     if (geometry.IsIntersecting()) {
       PhysicalRect intersection_rect = PhysicalRect::EnclosingRect(
           matrix
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index 2f7819d0..3e24e52 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -149,7 +149,7 @@
   owner_layout_object->MapLocalToAncestor(nullptr, local_root_transform_state,
                                           kTraverseDocumentBoundaries);
   TransformationMatrix matrix =
-      local_root_transform_state.AccumulatedTransform().Inverse();
+      local_root_transform_state.AccumulatedTransform().InverseOrIdentity();
   PhysicalRect local_viewport_rect = PhysicalRect::EnclosingRect(
       matrix.ProjectQuad(gfx::QuadF(gfx::RectF(viewport_rect))).BoundingBox());
   gfx::Rect compositing_rect = ToEnclosingRect(local_viewport_rect);
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
index 686ddb6..398febd 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
@@ -582,7 +582,7 @@
     const StyleRecalcContext& style_recalc_context) {
   // TODO(crbug.com/1181868): This is a kind of layout. We might want to
   // introduce new LayoutObject.
-  scoped_refptr<ComputedStyle> style =
+  scoped_refptr<const ComputedStyle> original_style =
       OriginalStyleForLayoutObject(style_recalc_context);
   float width = 0;
   for (Node* child = FieldsWrapperElement()->firstChild(); child;
@@ -594,17 +594,18 @@
       // We need to pass the ComputedStyle of this element because child
       // elements can't resolve inherited style at this timing.
       width += static_cast<DateTimeFieldElement*>(child_element)
-                   ->MaximumWidth(*style);
+                   ->MaximumWidth(*original_style);
     } else {
       // ::-webkit-datetime-edit-text case. It has no
       // border/padding/margin in html.css.
       width += DateTimeFieldElement::ComputeTextWidth(
-          *style, child_element->textContent());
+          *original_style, child_element->textContent());
     }
   }
-  style->SetWidth(Length::Fixed(ceilf(width)));
-  style->SetCustomStyleCallbackDependsOnFont();
-  return style;
+  ComputedStyleBuilder builder(*original_style);
+  builder.SetWidth(Length::Fixed(ceilf(width)));
+  builder.SetCustomStyleCallbackDependsOnFont();
+  return builder.TakeStyle();
 }
 
 void DateTimeEditElement::DidBlurFromField(mojom::blink::FocusType focus_type) {
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index a8328e8a..bb7b41ca 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -213,8 +213,11 @@
   }
 }
 
-void FileInputType::CustomStyleForLayoutObject(ComputedStyle& style) {
-  style.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+scoped_refptr<ComputedStyle> FileInputType::CustomStyleForLayoutObject(
+    scoped_refptr<ComputedStyle> original_style) {
+  ComputedStyleBuilder builder(*original_style);
+  builder.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+  return builder.TakeStyle();
 }
 
 LayoutObject* FileInputType::CreateLayoutObject(const ComputedStyle& style,
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.h b/third_party/blink/renderer/core/html/forms/file_input_type.h
index 46281970..773d3fc 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.h
@@ -73,7 +73,8 @@
   String ValueMissingText() const override;
   void HandleDOMActivateEvent(Event&) override;
   void OpenPopupView() override;
-  void CustomStyleForLayoutObject(ComputedStyle& style) override;
+  scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
+      scoped_refptr<ComputedStyle> original_style) override;
   LayoutObject* CreateLayoutObject(const ComputedStyle&,
                                    LegacyLayout) const override;
   FileList* Files() override;
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
index a8912ea..f7a6f0d 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
@@ -87,10 +87,9 @@
   EXPECT_EQ(TextDirection::kRtl, message_dir);
   EXPECT_EQ(TextDirection::kLtr, sub_message_dir);
 
-  scoped_refptr<ComputedStyle> rtl_style =
-      ComputedStyle::Clone(input->GetLayoutObject()->StyleRef());
-  rtl_style->SetDirection(TextDirection::kRtl);
-  input->GetLayoutObject()->SetStyle(std::move(rtl_style));
+  ComputedStyleBuilder rtl_style_builder(input->GetLayoutObject()->StyleRef());
+  rtl_style_builder.SetDirection(TextDirection::kRtl);
+  input->GetLayoutObject()->SetStyle(rtl_style_builder.TakeStyle());
   input->FindCustomValidationMessageTextDirection(message, message_dir,
                                                   sub_message, sub_message_dir);
   EXPECT_EQ(TextDirection::kRtl, message_dir);
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index 21daa0d3..f165d36d 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -2184,10 +2184,10 @@
 
 scoped_refptr<ComputedStyle> HTMLInputElement::CustomStyleForLayoutObject(
     const StyleRecalcContext& style_recalc_context) {
-  scoped_refptr<ComputedStyle> style =
+  scoped_refptr<ComputedStyle> original_style =
       OriginalStyleForLayoutObject(style_recalc_context);
-  input_type_view_->CustomStyleForLayoutObject(*style);
-  return style;
+  return input_type_view_->CustomStyleForLayoutObject(
+      std::move(original_style));
 }
 
 void HTMLInputElement::DidNotifySubtreeInsertionsToDocument() {
diff --git a/third_party/blink/renderer/core/html/forms/image_input_type.cc b/third_party/blink/renderer/core/html/forms/image_input_type.cc
index bd271cb..cf437ea 100644
--- a/third_party/blink/renderer/core/html/forms/image_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/image_input_type.cc
@@ -276,9 +276,14 @@
   HTMLImageFallbackHelper::CreateAltTextShadowTree(GetElement());
 }
 
-void ImageInputType::CustomStyleForLayoutObject(ComputedStyle& style) {
-  if (use_fallback_content_)
-    HTMLImageFallbackHelper::CustomStyleForAltText(GetElement(), style);
+scoped_refptr<ComputedStyle> ImageInputType::CustomStyleForLayoutObject(
+    scoped_refptr<ComputedStyle> original_style) {
+  if (!use_fallback_content_)
+    return original_style;
+
+  ComputedStyleBuilder builder(*original_style);
+  HTMLImageFallbackHelper::CustomStyleForAltText(GetElement(), builder);
+  return builder.TakeStyle();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/image_input_type.h b/third_party/blink/renderer/core/html/forms/image_input_type.h
index b943a39..d6c4a47 100644
--- a/third_party/blink/renderer/core/html/forms/image_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/image_input_type.h
@@ -41,7 +41,8 @@
 class ImageInputType final : public BaseButtonInputType {
  public:
   explicit ImageInputType(HTMLInputElement&);
-  void CustomStyleForLayoutObject(ComputedStyle& style) override;
+  scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
+      scoped_refptr<ComputedStyle> original_style) override;
 
  private:
   void CountUsage() override;
diff --git a/third_party/blink/renderer/core/html/forms/input_type_view.cc b/third_party/blink/renderer/core/html/forms/input_type_view.cc
index f285ce0..661c0b7 100644
--- a/third_party/blink/renderer/core/html/forms/input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/input_type_view.cc
@@ -98,7 +98,10 @@
   return LayoutObject::CreateObject(&GetElement(), style, legacy);
 }
 
-void InputTypeView::CustomStyleForLayoutObject(ComputedStyle&) {}
+scoped_refptr<ComputedStyle> InputTypeView::CustomStyleForLayoutObject(
+    scoped_refptr<ComputedStyle> original_style) {
+  return original_style;
+}
 
 ControlPart InputTypeView::AutoAppearance() const {
   return kNoControlPart;
diff --git a/third_party/blink/renderer/core/html/forms/input_type_view.h b/third_party/blink/renderer/core/html/forms/input_type_view.h
index 7bb5707..b1679eb6 100644
--- a/third_party/blink/renderer/core/html/forms/input_type_view.h
+++ b/third_party/blink/renderer/core/html/forms/input_type_view.h
@@ -109,7 +109,8 @@
   virtual void SubtreeHasChanged();
   virtual LayoutObject* CreateLayoutObject(const ComputedStyle&,
                                            LegacyLayout) const;
-  virtual void CustomStyleForLayoutObject(ComputedStyle& style);
+  virtual scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
+      scoped_refptr<ComputedStyle> original_style);
   virtual ControlPart AutoAppearance() const;
   virtual TextDirection ComputedTextDirection();
   virtual void OpenPopupView();
diff --git a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
index f89cf1ef..d8d16ed 100644
--- a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
@@ -376,17 +376,20 @@
   ClosePopupView();
 }
 
-void MultipleFieldsTemporalInputTypeView::CustomStyleForLayoutObject(
-    ComputedStyle& style) {
-  EDisplay original_display = style.Display();
+scoped_refptr<ComputedStyle>
+MultipleFieldsTemporalInputTypeView::CustomStyleForLayoutObject(
+    scoped_refptr<ComputedStyle> original_style) {
+  EDisplay original_display = original_style->Display();
   EDisplay new_display = original_display;
   if (original_display == EDisplay::kInline ||
       original_display == EDisplay::kInlineBlock)
     new_display = EDisplay::kInlineFlex;
   else if (original_display == EDisplay::kBlock)
     new_display = EDisplay::kFlex;
-  style.SetDisplay(new_display);
-  style.SetDirection(ComputedTextDirection());
+  ComputedStyleBuilder builder(*original_style);
+  builder.SetDisplay(new_display);
+  builder.SetDirection(ComputedTextDirection());
+  return builder.TakeStyle();
 }
 
 void MultipleFieldsTemporalInputTypeView::CreateShadowSubtree() {
diff --git a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h
index c00ad8f2..ffe02b5 100644
--- a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h
+++ b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h
@@ -98,7 +98,8 @@
   void OpenPopupView() override;
   void ClosePopupView() override;
   bool HasOpenedPopup() const override;
-  void CustomStyleForLayoutObject(ComputedStyle& style) override;
+  scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
+      scoped_refptr<ComputedStyle> original_style) override;
   void CreateShadowSubtree() final;
   void DestroyShadowSubtree() final;
   void DisabledAttributeChanged() final;
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index 838879e..be4bd2d 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -509,16 +509,16 @@
       ((option_style->Direction() != inner_style->Direction() ||
         option_style->GetUnicodeBidi() != inner_style->GetUnicodeBidi() ||
         option_style->GetTextAlign(true) != inner_style->GetTextAlign(true)))) {
-    scoped_refptr<ComputedStyle> cloned_style =
-        ComputedStyle::Clone(*inner_style);
-    cloned_style->SetDirection(option_style->Direction());
-    cloned_style->SetUnicodeBidi(option_style->GetUnicodeBidi());
-    cloned_style->SetTextAlign(option_style->GetTextAlign(true));
+    ComputedStyleBuilder builder(*inner_style);
+    builder.SetDirection(option_style->Direction());
+    builder.SetUnicodeBidi(option_style->GetUnicodeBidi());
+    builder.SetTextAlign(option_style->GetTextAlign(true));
+    scoped_refptr<const ComputedStyle> new_style = builder.TakeStyle();
     if (auto* inner_layout = inner_element.GetLayoutObject()) {
       inner_layout->SetModifiedStyleOutsideStyleRecalc(
-          std::move(cloned_style), LayoutObject::ApplyStyleChanges::kYes);
+          std::move(new_style), LayoutObject::ApplyStyleChanges::kYes);
     } else {
-      inner_element.SetComputedStyle(std::move(cloned_style));
+      inner_element.SetComputedStyle(std::move(new_style));
     }
   }
   if (select_->GetLayoutObject())
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
index ffd1759..a7f27bd 100644
--- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
+++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -290,21 +290,21 @@
   Element* host = OwnerShadowHost();
   DCHECK(host);
   const ComputedStyle& host_style = host->ComputedStyleRef();
-  scoped_refptr<ComputedStyle> style =
-      OriginalStyleForLayoutObject(style_recalc_context);
+  ComputedStyleBuilder builder(
+      *OriginalStyleForLayoutObject(style_recalc_context));
 
   if (host_style.EffectiveAppearance() == kSliderVerticalPart)
-    style->SetEffectiveAppearance(kSliderThumbVerticalPart);
+    builder.SetEffectiveAppearance(kSliderThumbVerticalPart);
   else if (host_style.EffectiveAppearance() == kSliderHorizontalPart)
-    style->SetEffectiveAppearance(kSliderThumbHorizontalPart);
+    builder.SetEffectiveAppearance(kSliderThumbHorizontalPart);
   else if (host_style.EffectiveAppearance() == kMediaSliderPart)
-    style->SetEffectiveAppearance(kMediaSliderThumbPart);
+    builder.SetEffectiveAppearance(kMediaSliderThumbPart);
   else if (host_style.EffectiveAppearance() == kMediaVolumeSliderPart)
-    style->SetEffectiveAppearance(kMediaVolumeSliderThumbPart);
-  if (style->HasEffectiveAppearance())
-    LayoutTheme::GetTheme().AdjustSliderThumbSize(*style);
+    builder.SetEffectiveAppearance(kMediaVolumeSliderThumbPart);
+  if (builder.InternalStyle()->HasEffectiveAppearance())
+    LayoutTheme::GetTheme().AdjustSliderThumbSize(builder);
 
-  return style;
+  return builder.TakeStyle();
 }
 
 // --------------------------------
diff --git a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
index ea4dca2..29097aa7 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
@@ -146,8 +146,8 @@
   // TODO(https://crbug.com/1101564): The custom inheritance done here means we
   // need to mark for style recalc inside style recalc. See the workaround in
   // LayoutTextControl::StyleDidChange.
-  text_block_style->SetDirection(start_style.Direction());
-  text_block_style->SetUnicodeBidi(start_style.GetUnicodeBidi());
+  text_block_style_builder.SetDirection(start_style.Direction());
+  text_block_style_builder.SetUnicodeBidi(start_style.GetUnicodeBidi());
   text_block_style_builder.SetUserSelect(EUserSelect::kText);
   text_block_style->SetUserModify(
       To<HTMLFormControlElement>(host)->IsDisabledOrReadOnly()
@@ -158,7 +158,7 @@
   text_block_style->SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
 
   if (!IsA<HTMLTextAreaElement>(host)) {
-    text_block_style->SetWhiteSpace(EWhiteSpace::kPre);
+    text_block_style_builder.SetWhiteSpace(EWhiteSpace::kPre);
     text_block_style_builder.SetOverflowWrap(EOverflowWrap::kNormal);
     text_block_style_builder.SetTextOverflow(
         ToTextControl(host)->ValueForTextOverflow());
@@ -206,7 +206,7 @@
 
   // Using StyleAdjuster::adjustComputedStyle updates unwanted style. We'd like
   // to apply only editing-related and alignment-related.
-  StyleAdjuster::AdjustStyleForEditing(*text_block_style);
+  StyleAdjuster::AdjustStyleForEditing(text_block_style_builder);
   if (!is_visible_)
     text_block_style_builder.SetOpacity(0);
 
diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
index 5475e65..42ba85db 100644
--- a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
@@ -289,10 +289,13 @@
   return InputTypeView::ShouldSubmitImplicitly(event);
 }
 
-void TextFieldInputType::CustomStyleForLayoutObject(ComputedStyle& style) {
+scoped_refptr<ComputedStyle> TextFieldInputType::CustomStyleForLayoutObject(
+    scoped_refptr<ComputedStyle> original_style) {
   // The flag is necessary in order that a text field <input> with non-'visible'
   // overflow property doesn't change its baseline.
-  style.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+  ComputedStyleBuilder builder(*original_style);
+  builder.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+  return builder.TakeStyle();
 }
 
 LayoutObject* TextFieldInputType::CreateLayoutObject(
diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.h b/third_party/blink/renderer/core/html/forms/text_field_input_type.h
index 20a921f..820d386 100644
--- a/third_party/blink/renderer/core/html/forms/text_field_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.h
@@ -68,7 +68,8 @@
                 TextFieldEventBehavior,
                 TextControlSetValueSelection) override;
   void UpdateView() override;
-  void CustomStyleForLayoutObject(ComputedStyle& style) override;
+  scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
+      scoped_refptr<ComputedStyle> original_style) override;
   LayoutObject* CreateLayoutObject(const ComputedStyle&,
                                    LegacyLayout) const override;
   ControlPart AutoAppearance() const override;
diff --git a/third_party/blink/renderer/core/html/html_html_element.cc b/third_party/blink/renderer/core/html/html_html_element.cc
index 3d66083..4d118c3 100644
--- a/third_party/blink/renderer/core/html/html_html_element.cc
+++ b/third_party/blink/renderer/core/html/html_html_element.cc
@@ -75,11 +75,11 @@
 scoped_refptr<const ComputedStyle> CreateLayoutStyle(
     const ComputedStyle& style,
     const ComputedStyle& propagated_style) {
-  scoped_refptr<ComputedStyle> layout_style = ComputedStyle::Clone(style);
-  layout_style->SetDirection(propagated_style.Direction());
-  layout_style->SetWritingMode(propagated_style.GetWritingMode());
-  layout_style->UpdateFontOrientation();
-  return layout_style;
+  ComputedStyleBuilder builder(style);
+  builder.SetDirection(propagated_style.Direction());
+  builder.SetWritingMode(propagated_style.GetWritingMode());
+  builder.MutableInternalStyle()->UpdateFontOrientation();
+  return builder.TakeStyle();
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index 0cc50785..d83d864 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -927,10 +927,10 @@
     case LayoutDisposition::kCollapsed:
       return OriginalStyleForLayoutObject(style_recalc_context);
     case LayoutDisposition::kFallbackContent: {
-      scoped_refptr<ComputedStyle> style =
-          OriginalStyleForLayoutObject(style_recalc_context);
-      HTMLImageFallbackHelper::CustomStyleForAltText(*this, *style);
-      return style;
+      ComputedStyleBuilder builder(
+          *OriginalStyleForLayoutObject(style_recalc_context));
+      HTMLImageFallbackHelper::CustomStyleForAltText(*this, builder);
+      return builder.TakeStyle();
     }
     default:
       NOTREACHED();
diff --git a/third_party/blink/renderer/core/html/html_image_fallback_helper.cc b/third_party/blink/renderer/core/html/html_image_fallback_helper.cc
index b7be6e25..5b31dee0 100644
--- a/third_party/blink/renderer/core/html/html_image_fallback_helper.cc
+++ b/third_party/blink/renderer/core/html/html_image_fallback_helper.cc
@@ -138,8 +138,9 @@
   element.EnsureUserAgentShadowRoot().AppendChild(container);
 }
 
-void HTMLImageFallbackHelper::CustomStyleForAltText(Element& element,
-                                                    ComputedStyle& new_style) {
+void HTMLImageFallbackHelper::CustomStyleForAltText(
+    Element& element,
+    ComputedStyleBuilder& builder) {
   // If we have an author shadow root or have not created the UA shadow root
   // yet, bail early. We can't use ensureUserAgentShadowRoot() here because that
   // would alter the DOM tree during style recalc.
@@ -165,20 +166,20 @@
   if (element.GetDocument().InQuirksMode()) {
     // Mimic the behaviour of the image host by setting symmetric dimensions if
     // only one dimension is specified.
-    if (!new_style.Width().IsAuto() && new_style.Height().IsAuto())
-      new_style.SetHeight(new_style.Width());
-    else if (!new_style.Height().IsAuto() && new_style.Width().IsAuto())
-      new_style.SetWidth(new_style.Height());
+    if (!builder.Width().IsAuto() && builder.Height().IsAuto())
+      builder.SetHeight(builder.Width());
+    else if (!builder.Height().IsAuto() && builder.Width().IsAuto())
+      builder.SetWidth(builder.Height());
 
-    if (!new_style.Width().IsAuto() && !new_style.Height().IsAuto())
+    if (!builder.Width().IsAuto() && !builder.Height().IsAuto())
       fallback.AlignToBaseline();
   }
 
   bool has_intrinsic_dimensions =
-      !new_style.Width().IsAuto() && !new_style.Height().IsAuto();
+      !builder.Width().IsAuto() && !builder.Height().IsAuto();
   bool has_dimensions_from_ar =
-      !new_style.AspectRatio().IsAuto() &&
-      (!new_style.Width().IsAuto() || !new_style.Height().IsAuto());
+      !builder.AspectRatio().IsAuto() &&
+      (!builder.Width().IsAuto() || !builder.Height().IsAuto());
   bool has_no_alt_attribute =
       element.getAttribute(html_names::kAltAttr).empty();
   bool treat_as_replaced =
@@ -193,24 +194,23 @@
     // attribute, or the Document is in quirks mode The user agent is expected
     // to treat the element as a replaced element whose content is the text that
     // the element represents, if any."
-    fallback.ShowAsReplaced(new_style.Width(), new_style.Height(),
-                            new_style.EffectiveZoom());
+    fallback.ShowAsReplaced(builder.Width(), builder.Height(),
+                            builder.EffectiveZoom());
 
     // 16px for the image and 2px for its top/left border/padding offset.
     int pixels_for_alt_image = 18;
-    if (ImageSmallerThanAltImage(pixels_for_alt_image, new_style.Width(),
-                                 new_style.Height())) {
+    if (ImageSmallerThanAltImage(pixels_for_alt_image, builder.Width(),
+                                 builder.Height())) {
       fallback.HideBrokenImageIcon();
     } else {
       fallback.ShowBorder();
-      fallback.ShowBrokenImageIcon(new_style.IsLeftToRightDirection());
+      fallback.ShowBrokenImageIcon(builder.Direction() == TextDirection::kLtr);
     }
   } else {
-    if (new_style.Display() == EDisplay::kInline) {
-      new_style.SetWidth(Length());
-      new_style.SetHeight(Length());
-      new_style.SetAspectRatio(
-          ComputedStyleInitialValues::InitialAspectRatio());
+    if (builder.Display() == EDisplay::kInline) {
+      builder.SetWidth(Length());
+      builder.SetHeight(Length());
+      builder.SetAspectRatio(ComputedStyleInitialValues::InitialAspectRatio());
     }
     if (ElementRepresentsNothing(element)) {
       // "If the element is an img element that represents nothing and the user
@@ -226,7 +226,7 @@
       // the text, optionally with an icon indicating that an image is missing,
       // so that the user can request the image be displayed or investigate why
       // it is not rendering."
-      fallback.ShowBrokenImageIcon(new_style.IsLeftToRightDirection());
+      fallback.ShowBrokenImageIcon(builder.Direction() == TextDirection::kLtr);
     }
   }
 }
diff --git a/third_party/blink/renderer/core/html/html_image_fallback_helper.h b/third_party/blink/renderer/core/html/html_image_fallback_helper.h
index c2b3e860..00f45ac 100644
--- a/third_party/blink/renderer/core/html/html_image_fallback_helper.h
+++ b/third_party/blink/renderer/core/html/html_image_fallback_helper.h
@@ -10,14 +10,14 @@
 namespace blink {
 
 class Element;
-class ComputedStyle;
+class ComputedStyleBuilder;
 
 class HTMLImageFallbackHelper {
   STATIC_ONLY(HTMLImageFallbackHelper);
 
  public:
   static void CreateAltTextShadowTree(Element&);
-  static void CustomStyleForAltText(Element&, ComputedStyle& new_style);
+  static void CustomStyleForAltText(Element&, ComputedStyleBuilder&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index 5c67b3c..952a621 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -379,7 +379,7 @@
           kTraverseDocumentBoundaries | kApplyRemoteMainFrameTransform);
       TransformationMatrix matrix =
           implicit_root_to_target_document_transform.AccumulatedTransform()
-              .Inverse();
+              .InverseOrIdentity();
       intersection_rect_ = PhysicalRect::EnclosingRect(
           matrix.ProjectQuad(gfx::QuadF(gfx::RectF(intersection_rect_)))
               .BoundingBox());
diff --git a/third_party/blink/renderer/core/layout/anchor_scroll_data.cc b/third_party/blink/renderer/core/layout/anchor_scroll_data.cc
index e85cd75..37c846a 100644
--- a/third_party/blink/renderer/core/layout/anchor_scroll_data.cc
+++ b/third_party/blink/renderer/core/layout/anchor_scroll_data.cc
@@ -42,6 +42,19 @@
   return nullptr;
 }
 
+// Returns the PaintLayer of the scroll container of an anchor-positioned |box|.
+const PaintLayer* ContainingScrollContainerLayerForAnchorScroll(
+    const LayoutBox* box) {
+  // Normally, |scroller_layer| is the result. There's only one special case
+  // where |box| is fixed-positioned and |scroller_layer| is the LayoutView,
+  // then |box| doesn't actually scroll with |scroller_layer|, and null should
+  // be returned.
+  bool is_fixed_to_view = false;
+  const PaintLayer* scroller_layer =
+      box->Layer()->ContainingScrollContainerLayer(&is_fixed_to_view);
+  return is_fixed_to_view ? nullptr : scroller_layer;
+}
+
 }  // namespace
 
 AnchorScrollData::AnchorScrollData(Element* element)
@@ -65,12 +78,14 @@
     const PaintLayer* starting_layer =
         anchor->ContainingScrollContainer()->Layer();
     const PaintLayer* bounding_layer =
-        owner_->GetLayoutBox()->Layer()->ContainingScrollContainerLayer();
+        ContainingScrollContainerLayerForAnchorScroll(owner_->GetLayoutBox());
     for (const PaintLayer* layer = starting_layer; layer != bounding_layer;
          layer = layer->ContainingScrollContainerLayer()) {
-      // |bounding_layer| must be an ancestor of |starting_layer|, so we'll
-      // never reach a null layer here.
+      // |bounding_layer| must be either null (for fixed-positioned |owner_|) or
+      // an ancestor of |starting_layer|, so we'll never have a null layer here.
       DCHECK(layer);
+      if (!layer->GetScrollableArea()->HasOverflow())
+        continue;
       new_scroll_container_layers.push_back(layer);
       new_accumulated_scroll_offset +=
           layer->GetScrollableArea()->GetScrollOffset();
diff --git a/third_party/blink/renderer/core/layout/anchor_scroll_data_test.cc b/third_party/blink/renderer/core/layout/anchor_scroll_data_test.cc
index f64952c..7f10b80 100644
--- a/third_party/blink/renderer/core/layout/anchor_scroll_data_test.cc
+++ b/third_party/blink/renderer/core/layout/anchor_scroll_data_test.cc
@@ -47,10 +47,11 @@
 
   SetBodyInnerHTML(R"HTML(
     <div style="position: relative">
-      <div style="overflow: scroll; height: 1px;">
+      <div style="overflow: scroll; height: 20px;">
         Lorem ipsum
         <span id="anchor" style="anchor-name: --a1">anchor</span>
         dolor sit amet
+        <div style="height: 100px"></div>
       </div>
       <div id="anchored" style="position: absolute; anchor-scroll: --a1">
         anchored
@@ -87,10 +88,11 @@
   SetBodyInnerHTML(R"HTML(
     <style>.anchored { position: absolute; anchor-scroll: --a1; }</style>
     <div style="position: relative>
-      <div style="overflow: scroll; height: 1px;">
+      <div style="overflow: scroll; height: 20px;">
         Lorem ipsum
         <span id="anchor" style="anchor-name: --a1">anchor</span>
         dolor sit amet
+        <div style="height: 100px"></div>
       </div>
 
       <div class="anchored" id="remove">Will be removed</div>
diff --git a/third_party/blink/renderer/core/layout/geometry/transform_state.cc b/third_party/blink/renderer/core/layout/geometry/transform_state.cc
index 6dbd142..72617705 100644
--- a/third_party/blink/renderer/core/layout/geometry/transform_state.cc
+++ b/third_party/blink/renderer/core/layout/geometry/transform_state.cc
@@ -164,9 +164,10 @@
                               ? accumulated_offset_
                               : -accumulated_offset_);
   if (accumulated_transform_) {
-    point = direction_ == kApplyTransformDirection
-                ? accumulated_transform_->MapPoint(point)
-                : accumulated_transform_->Inverse().ProjectPoint(point);
+    point =
+        direction_ == kApplyTransformDirection
+            ? accumulated_transform_->MapPoint(point)
+            : accumulated_transform_->InverseOrIdentity().ProjectPoint(point);
   }
   return PhysicalOffset::FromPointFRound(point);
 }
@@ -182,7 +183,7 @@
   if (direction_ == kApplyTransformDirection)
     return accumulated_transform_->MapQuad(quad);
 
-  return accumulated_transform_->Inverse().ProjectQuad(quad);
+  return accumulated_transform_->InverseOrIdentity().ProjectQuad(quad);
 }
 
 const TransformationMatrix& TransformState::AccumulatedTransform() const {
@@ -197,11 +198,12 @@
     if (map_quad_)
       last_planar_quad_ = t.MapQuad(last_planar_quad_);
   } else {
-    TransformationMatrix inverse_transform = t.Inverse();
-    if (map_point_)
-      last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
-    if (map_quad_) {
-      last_planar_quad_ = inverse_transform.ProjectQuad(last_planar_quad_);
+    TransformationMatrix inverse_transform;
+    if (t.GetInverse(&inverse_transform)) {
+      if (map_point_)
+        last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
+      if (map_quad_)
+        last_planar_quad_ = inverse_transform.ProjectQuad(last_planar_quad_);
     }
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index 30e3042..25afe0a 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -240,10 +240,9 @@
         InFlowPositionedInlineAncestor(block_flow->InlineElementContinuation()))
       continue;
 
-    scoped_refptr<ComputedStyle> new_block_style =
-        ComputedStyle::Clone(block->StyleRef());
-    new_block_style->SetPosition(new_style.GetPosition());
-    block->SetStyle(new_block_style);
+    ComputedStyleBuilder new_block_style_builder(block->StyleRef());
+    new_block_style_builder.SetPosition(new_style.GetPosition());
+    block->SetStyle(new_block_style_builder.TakeStyle());
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_list_item.cc b/third_party/blink/renderer/core/layout/layout_list_item.cc
index 826cc78..d2b6f3d 100644
--- a/third_party/blink/renderer/core/layout/layout_list_item.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_item.cc
@@ -235,10 +235,13 @@
   if (layout_object.StyleRef().LogicalHeight() == height)
     return;
 
-  scoped_refptr<ComputedStyle> new_style =
-      ComputedStyle::Clone(layout_object.StyleRef());
-  new_style->SetLogicalHeight(height);
-  layout_object.SetStyle(std::move(new_style),
+  ComputedStyleBuilder builder(layout_object.StyleRef());
+  if (layout_object.IsHorizontalWritingMode()) {
+    builder.SetHeight(height);
+  } else {
+    builder.SetWidth(height);
+  }
+  layout_object.SetStyle(builder.TakeStyle(),
                          LayoutObject::ApplyStyleChanges::kNo);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 8c33d6bb..93c53aa8 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -2507,15 +2507,15 @@
   // avoid getting an inline with positioning or an invalid display.
   //
   if (IsImage() || IsQuote()) {
-    scoped_refptr<ComputedStyle> style =
-        GetDocument().GetStyleResolver().CreateComputedStyle();
-    style->InheritFrom(*pseudo_style);
+    ComputedStyleBuilder builder =
+        GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+    builder.MutableInternalStyle()->InheritFrom(*pseudo_style);
     if (match_parent_size) {
       DCHECK(IsImage());
-      style->SetWidth(Length::Percent(100));
-      style->SetHeight(Length::Percent(100));
+      builder.SetWidth(Length::Percent(100));
+      builder.SetHeight(Length::Percent(100));
     }
-    SetStyle(std::move(style));
+    SetStyle(builder.TakeStyle());
     return;
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object_test.cc b/third_party/blink/renderer/core/layout/layout_object_test.cc
index 62ec2b4..51cc4ee0 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -783,7 +783,10 @@
   EXPECT_EQ(PhysicalRect(10, 10, 20, 20), mock_object->LocalVisualRect());
   EXPECT_EQ(PhysicalRect(10, 10, 20, 20), mock_object->LocalVisualRect());
 
-  style->SetVisibility(EVisibility::kHidden);
+  ComputedStyleBuilder builder(*style);
+  builder.SetVisibility(EVisibility::kHidden);
+  mock_object->SetStyle(builder.TakeStyle(),
+                        LayoutObject::ApplyStyleChanges::kNo);
   EXPECT_CALL(*mock_object, VisualRectRespectsVisibility())
       .WillOnce(Return(true));
   EXPECT_TRUE(mock_object->LocalVisualRect().IsEmpty());
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell.cc b/third_party/blink/renderer/core/layout/layout_table_cell.cc
index 551b23e..0209b4bb 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_cell.cc
@@ -462,10 +462,10 @@
 void LayoutTableCell::UpdateStyleWritingModeFromRow(const LayoutObject* row) {
   NOT_DESTROYED();
   DCHECK_NE(StyleRef().GetWritingMode(), row->StyleRef().GetWritingMode());
-  scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(StyleRef());
-  new_style->SetWritingMode(row->StyleRef().GetWritingMode());
-  new_style->UpdateFontOrientation();
-  SetStyle(new_style, LayoutObject::ApplyStyleChanges::kNo);
+  ComputedStyleBuilder new_style_builder(StyleRef());
+  new_style_builder.SetWritingMode(row->StyleRef().GetWritingMode());
+  new_style_builder.MutableInternalStyle()->UpdateFontOrientation();
+  SetStyle(new_style_builder.TakeStyle(), LayoutObject::ApplyStyleChanges::kNo);
   SetHorizontalWritingMode(StyleRef().IsHorizontalWritingMode());
   UnmarkOrthogonalWritingModeRoot();
 
diff --git a/third_party/blink/renderer/core/layout/layout_table_row.cc b/third_party/blink/renderer/core/layout/layout_table_row.cc
index 94f6572..14aba72 100644
--- a/third_party/blink/renderer/core/layout/layout_table_row.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_row.cc
@@ -69,9 +69,9 @@
 
   // Legacy tables cannont handle relative/fixed rows.
   if (StyleRef().HasInFlowPosition()) {
-    scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(StyleRef());
-    new_style->SetPosition(EPosition::kStatic);
-    SetStyle(new_style, LayoutObject::ApplyStyleChanges::kNo);
+    ComputedStyleBuilder builder(StyleRef());
+    builder.SetPosition(EPosition::kStatic);
+    SetStyle(builder.TakeStyle(), LayoutObject::ApplyStyleChanges::kNo);
   }
 
   LayoutTableBoxComponent::StyleDidChange(diff, old_style);
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.cc b/third_party/blink/renderer/core/layout/layout_table_section.cc
index 73a4980..9bf9637 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section.cc
@@ -124,9 +124,9 @@
 
   // Legacy tables cannot handle relative/sticky sections.
   if (StyleRef().HasInFlowPosition()) {
-    scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(StyleRef());
-    new_style->SetPosition(EPosition::kStatic);
-    SetStyle(new_style, LayoutObject::ApplyStyleChanges::kNo);
+    ComputedStyleBuilder builder(StyleRef());
+    builder.SetPosition(EPosition::kStatic);
+    SetStyle(builder.TakeStyle(), LayoutObject::ApplyStyleChanges::kNo);
   }
 
   LayoutTableBoxComponent::StyleDidChange(diff, old_style);
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index 5ab89da..ca131cb 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -270,9 +270,9 @@
       return AdjustMenuListButtonStyle(builder);
     case kSliderThumbHorizontalPart:
     case kSliderThumbVerticalPart:
-      return AdjustSliderThumbStyle(style);
+      return AdjustSliderThumbStyle(builder);
     case kSearchFieldCancelButtonPart:
-      return AdjustSearchFieldCancelButtonStyle(style);
+      return AdjustSearchFieldCancelButtonStyle(builder);
     default:
       break;
   }
@@ -466,13 +466,13 @@
   DCHECK(IsSliderContainer(element));
 
   if (style.EffectiveAppearance() == kSliderVerticalPart) {
-    style.SetTouchAction(TouchAction::kPanX);
-    style.SetWritingMode(WritingMode::kVerticalRl);
+    builder.SetTouchAction(TouchAction::kPanX);
+    builder.SetWritingMode(WritingMode::kVerticalRl);
     // It's always in RTL because the slider value increases up even in LTR.
-    style.SetDirection(TextDirection::kRtl);
+    builder.SetDirection(TextDirection::kRtl);
   } else {
-    style.SetTouchAction(TouchAction::kPanY);
-    style.SetWritingMode(WritingMode::kHorizontalTb);
+    builder.SetTouchAction(TouchAction::kPanY);
+    builder.SetWritingMode(WritingMode::kHorizontalTb);
     if (To<HTMLInputElement>(element.OwnerShadowHost())->list()) {
       builder.SetAlignSelf(StyleSelfAlignmentData(ItemPosition::kCenter,
                                                   OverflowAlignment::kUnsafe));
@@ -481,13 +481,14 @@
   style.SetEffectiveAppearance(kNoControlPart);
 }
 
-void LayoutTheme::AdjustSliderThumbStyle(ComputedStyle& style) const {
-  AdjustSliderThumbSize(style);
+void LayoutTheme::AdjustSliderThumbStyle(ComputedStyleBuilder& builder) const {
+  AdjustSliderThumbSize(builder);
 }
 
-void LayoutTheme::AdjustSliderThumbSize(ComputedStyle&) const {}
+void LayoutTheme::AdjustSliderThumbSize(ComputedStyleBuilder&) const {}
 
-void LayoutTheme::AdjustSearchFieldCancelButtonStyle(ComputedStyle&) const {}
+void LayoutTheme::AdjustSearchFieldCancelButtonStyle(
+    ComputedStyleBuilder&) const {}
 
 void LayoutTheme::PlatformColorsDidChange() {
   UpdateForcedColorsState();
diff --git a/third_party/blink/renderer/core/layout/layout_theme.h b/third_party/blink/renderer/core/layout/layout_theme.h
index 16c459e..04220a8 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.h
+++ b/third_party/blink/renderer/core/layout/layout_theme.h
@@ -145,7 +145,7 @@
   virtual Color SystemColor(CSSValueID,
                             mojom::blink::ColorScheme color_scheme) const;
 
-  virtual void AdjustSliderThumbSize(ComputedStyle&) const;
+  virtual void AdjustSliderThumbSize(ComputedStyleBuilder&) const;
 
   virtual int PopupInternalPaddingStart(const ComputedStyle&) const {
     return 0;
@@ -222,8 +222,8 @@
   virtual void AdjustSliderContainerStyle(const Element&,
                                           ComputedStyle&,
                                           ComputedStyleBuilder&) const;
-  virtual void AdjustSliderThumbStyle(ComputedStyle&) const;
-  virtual void AdjustSearchFieldCancelButtonStyle(ComputedStyle&) const;
+  virtual void AdjustSliderThumbStyle(ComputedStyleBuilder&) const;
+  virtual void AdjustSearchFieldCancelButtonStyle(ComputedStyleBuilder&) const;
 
   bool HasCustomFocusRingColor() const;
   Color GetCustomFocusRingColor() const;
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.cc b/third_party/blink/renderer/core/layout/layout_theme_default.cc
index 17f62c0..de8b873 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.cc
@@ -146,17 +146,18 @@
   return 7;
 }
 
-void LayoutThemeDefault::AdjustSliderThumbSize(ComputedStyle& style) const {
+void LayoutThemeDefault::AdjustSliderThumbSize(
+    ComputedStyleBuilder& builder) const {
   gfx::Size size = WebThemeEngineHelper::GetNativeThemeEngine()->GetSize(
       WebThemeEngine::kPartSliderThumb);
 
-  float zoom_level = style.EffectiveZoom();
-  if (style.EffectiveAppearance() == kSliderThumbHorizontalPart) {
-    style.SetWidth(Length::Fixed(size.width() * zoom_level));
-    style.SetHeight(Length::Fixed(size.height() * zoom_level));
-  } else if (style.EffectiveAppearance() == kSliderThumbVerticalPart) {
-    style.SetWidth(Length::Fixed(size.height() * zoom_level));
-    style.SetHeight(Length::Fixed(size.width() * zoom_level));
+  float zoom_level = builder.EffectiveZoom();
+  if (builder.EffectiveAppearance() == kSliderThumbHorizontalPart) {
+    builder.SetWidth(Length::Fixed(size.width() * zoom_level));
+    builder.SetHeight(Length::Fixed(size.height() * zoom_level));
+  } else if (builder.EffectiveAppearance() == kSliderThumbVerticalPart) {
+    builder.SetWidth(Length::Fixed(size.height() * zoom_level));
+    builder.SetHeight(Length::Fixed(size.width() * zoom_level));
   }
 }
 
@@ -199,14 +200,15 @@
 }
 
 void LayoutThemeDefault::AdjustSearchFieldCancelButtonStyle(
-    ComputedStyle& style) const {
+    ComputedStyleBuilder& builder) const {
   // Scale the button size based on the font size
-  float font_scale = style.FontSize() / kDefaultControlFontPixelSize;
+  float font_scale =
+      builder.InternalStyle()->FontSize() / kDefaultControlFontPixelSize;
   int cancel_button_size = static_cast<int>(lroundf(std::min(
       std::max(kMinCancelButtonSize, kDefaultCancelButtonSize * font_scale),
       kMaxCancelButtonSize)));
-  style.SetWidth(Length::Fixed(cancel_button_size));
-  style.SetHeight(Length::Fixed(cancel_button_size));
+  builder.SetWidth(Length::Fixed(cancel_button_size));
+  builder.SetHeight(Length::Fixed(cancel_button_size));
 }
 
 void LayoutThemeDefault::AdjustMenuListStyle(
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.h b/third_party/blink/renderer/core/layout/layout_theme_default.h
index bb2c28d3..e7e1af5 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.h
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.h
@@ -59,7 +59,7 @@
 
   gfx::Size SliderTickSize() const override;
   int SliderTickOffsetFromTrackCenter() const override;
-  void AdjustSliderThumbSize(ComputedStyle&) const override;
+  void AdjustSliderThumbSize(ComputedStyleBuilder&) const override;
 
   void AdjustInnerSpinButtonStyle(ComputedStyleBuilder&) const override;
   void AdjustButtonStyle(ComputedStyleBuilder&) const override;
@@ -74,7 +74,7 @@
                           Color inactive_foreground_color) override;
   Color PlatformFocusRingColor() const override;
 
-  void AdjustSearchFieldCancelButtonStyle(ComputedStyle&) const override;
+  void AdjustSearchFieldCancelButtonStyle(ComputedStyleBuilder&) const override;
 
   // MenuList refers to an unstyled menulist (meaning a menulist without
   // background-color or border set) and MenuListButton refers to a styled
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc
index 8ef4ebf..3a553699 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc
@@ -47,16 +47,19 @@
   LayoutBlockFlow* GetLayoutBlockFlow() const { return block_flow_; }
 
   void SetWhiteSpace(EWhiteSpace whitespace) {
-    style_->SetWhiteSpace(whitespace);
+    ComputedStyleBuilder builder(*style_);
+    builder.SetWhiteSpace(whitespace);
+    style_ = builder.TakeStyle();
+    block_flow_->SetStyle(style_, LayoutObject::ApplyStyleChanges::kNo);
   }
 
-  scoped_refptr<ComputedStyle> GetStyle(EWhiteSpace whitespace) {
+  scoped_refptr<const ComputedStyle> GetStyle(EWhiteSpace whitespace) {
     if (whitespace == EWhiteSpace::kNormal)
       return style_;
-    scoped_refptr<ComputedStyle> style(
-        GetDocument().GetStyleResolver().CreateComputedStyle());
-    style->SetWhiteSpace(whitespace);
-    return style;
+    ComputedStyleBuilder builder =
+        GetDocument().GetStyleResolver().CreateComputedStyleBuilder();
+    builder.SetWhiteSpace(whitespace);
+    return builder.TakeStyle();
   }
 
   bool HasRuby(const NGInlineItemsBuilder& builder) const {
@@ -466,10 +469,12 @@
 TEST_F(NGInlineItemsBuilderTest, BidiBlockOverride) {
   HeapVector<NGInlineItem> items;
   NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
-  scoped_refptr<ComputedStyle> block_style(
-      GetDocument().GetStyleResolver().CreateComputedStyle());
-  block_style->SetUnicodeBidi(UnicodeBidi::kBidiOverride);
-  block_style->SetDirection(TextDirection::kRtl);
+  ComputedStyleBuilder block_style_builder(
+      *GetDocument().GetStyleResolver().CreateComputedStyle());
+  block_style_builder.SetUnicodeBidi(UnicodeBidi::kBidiOverride);
+  block_style_builder.SetDirection(TextDirection::kRtl);
+  scoped_refptr<const ComputedStyle> block_style =
+      block_style_builder.TakeStyle();
   builder.EnterBlock(block_style.get());
   AppendText("Hello", &builder);
   builder.ExitBlock();
@@ -484,12 +489,12 @@
 
 static LayoutInline* CreateLayoutInline(
     Document* document,
-    void (*initialize_style)(ComputedStyle*)) {
-  scoped_refptr<ComputedStyle> style(
-      document->GetStyleResolver().CreateComputedStyle());
-  initialize_style(style.get());
+    void (*initialize_style)(ComputedStyleBuilder&)) {
+  ComputedStyleBuilder builder =
+      document->GetStyleResolver().CreateComputedStyleBuilder();
+  initialize_style(builder);
   LayoutInline* const node = LayoutInline::CreateAnonymous(document);
-  node->SetStyle(std::move(style), LayoutObject::ApplyStyleChanges::kNo);
+  node->SetStyle(builder.TakeStyle(), LayoutObject::ApplyStyleChanges::kNo);
   node->SetIsInLayoutNGInlineFormattingContext(true);
   return node;
 }
@@ -499,9 +504,9 @@
   NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
   AppendText("Hello ", &builder);
   LayoutInline* const isolate_rtl =
-      CreateLayoutInline(&GetDocument(), [](ComputedStyle* style) {
-        style->SetUnicodeBidi(UnicodeBidi::kIsolate);
-        style->SetDirection(TextDirection::kRtl);
+      CreateLayoutInline(&GetDocument(), [](ComputedStyleBuilder& builder) {
+        builder.SetUnicodeBidi(UnicodeBidi::kIsolate);
+        builder.SetDirection(TextDirection::kRtl);
       });
   builder.EnterInline(isolate_rtl);
   AppendText(u"\u05E2\u05D1\u05E8\u05D9\u05EA", &builder);
@@ -524,9 +529,9 @@
   NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
   AppendText("Hello ", &builder);
   LayoutInline* const isolate_override_rtl =
-      CreateLayoutInline(&GetDocument(), [](ComputedStyle* style) {
-        style->SetUnicodeBidi(UnicodeBidi::kIsolateOverride);
-        style->SetDirection(TextDirection::kRtl);
+      CreateLayoutInline(&GetDocument(), [](ComputedStyleBuilder& builder) {
+        builder.SetUnicodeBidi(UnicodeBidi::kIsolateOverride);
+        builder.SetDirection(TextDirection::kRtl);
       });
   builder.EnterInline(isolate_override_rtl);
   AppendText(u"\u05E2\u05D1\u05E8\u05D9\u05EA", &builder);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
index 042bde1..688008a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
@@ -257,6 +257,10 @@
 
   void SetShouldPropagateChildBreakValues(
       bool propagate_child_break_values = true) {
+    // Don't create rare data if `propagate_child_break_values` is already
+    // false.
+    if (!space_.HasRareData() && !propagate_child_break_values)
+      return;
     space_.EnsureRareData()->propagate_child_break_values =
         propagate_child_break_values;
   }
diff --git a/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc b/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc
index 184c565..64f363e3 100644
--- a/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc
+++ b/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc
@@ -53,20 +53,24 @@
 }
 
 void HitTestingTransformState::Flatten() {
-  TransformationMatrix inverse_transform = accumulated_transform_.Inverse();
-  last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
-  last_planar_quad_ = inverse_transform.ProjectQuad(last_planar_quad_);
-  last_planar_area_ = inverse_transform.ProjectQuad(last_planar_area_);
+  TransformationMatrix inverse_transform;
+  if (accumulated_transform_.GetInverse(&inverse_transform)) {
+    last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
+    last_planar_quad_ = inverse_transform.ProjectQuad(last_planar_quad_);
+    last_planar_area_ = inverse_transform.ProjectQuad(last_planar_area_);
+  }
 
   accumulated_transform_.MakeIdentity();
 }
 
 gfx::PointF HitTestingTransformState::MappedPoint() const {
-  return accumulated_transform_.Inverse().ProjectPoint(last_planar_point_);
+  return accumulated_transform_.InverseOrIdentity().ProjectPoint(
+      last_planar_point_);
 }
 
 gfx::QuadF HitTestingTransformState::MappedQuad() const {
-  return accumulated_transform_.Inverse().ProjectQuad(last_planar_quad_);
+  return accumulated_transform_.InverseOrIdentity().ProjectQuad(
+      last_planar_quad_);
 }
 
 PhysicalRect HitTestingTransformState::BoundsOfMappedQuad() const {
@@ -80,7 +84,7 @@
 PhysicalRect HitTestingTransformState::BoundsOfMappedQuadInternal(
     const gfx::QuadF& q) const {
   gfx::RectF rect =
-      accumulated_transform_.Inverse().ProjectQuad(q).BoundingBox();
+      accumulated_transform_.InverseOrIdentity().ProjectQuad(q).BoundingBox();
   PhysicalOffset offset(LayoutUnit::FromFloatRound(rect.x()),
                         LayoutUnit::FromFloatRound(rect.y()));
   PhysicalSize size(LayoutUnit::FromFloatRound(rect.right()) - offset.left,
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc
index a111d7e..14bc17da 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_combine_painter.cc
@@ -105,10 +105,10 @@
 
   const NGTextDecorationOffset decoration_offset(style_, style_);
 
-  // Paint text decorations except line through
-  PaintDecorationsExceptLineThrough(
-      NGTextFragmentPaintInfo{}, decoration_offset, decoration_info,
-      ~TextDecorationLine::kNone, paint_info, text_style);
+  // Paint underline and overline text decorations
+  PaintUnderOrOverLineDecorations(NGTextFragmentPaintInfo{}, decoration_offset,
+                                  decoration_info, ~TextDecorationLine::kNone,
+                                  paint_info, text_style);
 
   // Paint line through if needed
   PaintDecorationsOnlyLineThrough(decoration_info, paint_info, text_style);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
index 61a51b5..897d917 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
@@ -285,7 +285,7 @@
                                          decoration_info, lines_to_paint,
                                          paint_info, text_style);
   } else {
-    NGTextPainterBase::PaintDecorationsExceptLineThrough(
+    NGTextPainterBase::PaintUnderOrOverLineDecorations(
         fragment_paint_info, decoration_offset, decoration_info, lines_to_paint,
         paint_info, text_style, nullptr);
   }
@@ -508,7 +508,7 @@
       if (SetupPaintForSvgText(state, graphics_context_, style_to_paint,
                                SvgPaintMode::kTextDecoration, *resource_mode,
                                flags)) {
-        NGTextPainterBase::PaintDecorationsExceptLineThrough(
+        NGTextPainterBase::PaintUnderOrOverLineDecorations(
             fragment_paint_info, decoration_offset, decoration_info,
             lines_to_paint, paint_info, text_style, &flags);
       }
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.cc b/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.cc
index 2066804..afecde7 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.cc
@@ -22,12 +22,12 @@
 
 namespace blink {
 
-// We have two functions to paint text decoations, because we should paint
+// We have two functions to paint text decorations, because we should paint
 // text and decorations in following order:
-//   1. Paint text decorations except line through
+//   1. Paint underline or overline text decorations
 //   2. Paint text
-//   3. Paint line throguh
-void NGTextPainterBase::PaintDecorationsExceptLineThrough(
+//   3. Paint line through
+void NGTextPainterBase::PaintUnderOrOverLineDecorations(
     const NGTextFragmentPaintInfo& fragment_paint_info,
     const TextDecorationOffsetBase& decoration_offset,
     TextDecorationInfo& decoration_info,
@@ -44,8 +44,72 @@
 
   GraphicsContext& context = paint_info.context;
   GraphicsContextStateSaver state_saver(context);
-  UpdateGraphicsContext(context, text_style, state_saver);
 
+  // Updating Graphics Context for text only (kTextProperOnly),
+  // instead of the default text and shadows (kBothShadowsAndTextProper),
+  // because shadows will be painted by
+  // NGTextPainterBase::PaintUnderOrOverLineDecorationShadows.
+  UpdateGraphicsContext(context, text_style, state_saver,
+                        ShadowMode::kTextProperOnly);
+
+  PaintUnderOrOverLineDecorationShadows(fragment_paint_info, decoration_offset,
+                                        decoration_info, lines_to_paint, flags,
+                                        text_style, context);
+
+  PaintUnderOrOverLineDecorations(fragment_paint_info, decoration_offset,
+                                  decoration_info, lines_to_paint, flags,
+                                  context);
+}
+
+void NGTextPainterBase::PaintUnderOrOverLineDecorationShadows(
+    const NGTextFragmentPaintInfo& fragment_paint_info,
+    const TextDecorationOffsetBase& decoration_offset,
+    TextDecorationInfo& decoration_info,
+    TextDecorationLine lines_to_paint,
+    const cc::PaintFlags* flags,
+    const TextPaintStyle& text_style,
+    GraphicsContext& context) {
+  if (text_style.shadow == nullptr)
+    return;
+
+  const ShadowList* shadow_list = text_style.shadow.get();
+  if (shadow_list == nullptr)
+    return;
+
+  for (const auto& shadow : shadow_list->Shadows()) {
+    const Color& color = shadow.GetColor().Resolve(text_style.current_color,
+                                                   text_style.color_scheme);
+    // Detect when there's no effective shadow.
+    if (color.IsTransparent())
+      continue;
+
+    const gfx::Vector2dF& offset = shadow.Location().OffsetFromOrigin();
+
+    float blur = shadow.Blur();
+    DCHECK_GE(blur, 0);
+    const auto sigma = BlurRadiusToStdDev(blur);
+
+    context.BeginLayer(
+        1.0f, SkBlendMode::kSrcOver, nullptr, kColorFilterNone,
+        sk_make_sp<DropShadowPaintFilter>(
+            offset.x(), offset.y(), sigma, sigma, color.toSkColor4f(),
+            DropShadowPaintFilter::ShadowMode::kDrawShadowOnly, nullptr));
+
+    PaintUnderOrOverLineDecorations(fragment_paint_info, decoration_offset,
+                                    decoration_info, lines_to_paint, flags,
+                                    context);
+
+    context.EndLayer();
+  }
+}
+
+void NGTextPainterBase::PaintUnderOrOverLineDecorations(
+    const NGTextFragmentPaintInfo& fragment_paint_info,
+    const TextDecorationOffsetBase& decoration_offset,
+    TextDecorationInfo& decoration_info,
+    TextDecorationLine lines_to_paint,
+    const cc::PaintFlags* flags,
+    GraphicsContext& context) {
   for (wtf_size_t i = 0; i < decoration_info.AppliedDecorationCount(); i++) {
     decoration_info.SetDecorationIndex(i);
     context.SetStrokeThickness(decoration_info.ResolvedThickness());
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.h b/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.h
index 3c13cb10..5208b3e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_painter_base.h
@@ -45,12 +45,12 @@
   ~NGTextPainterBase() = default;
 
  protected:
-  // We have two functions to paint text decoations, because we should paint
+  // We have two functions to paint text decorations, because we should paint
   // text and decorations in following order:
-  //   1. Paint text decorations except line through
+  //   1. Paint underline or overline text decorations
   //   2. Paint text
-  //   3. Paint line through
-  void PaintDecorationsExceptLineThrough(
+  //   3. Paint line through text decoration
+  void PaintUnderOrOverLineDecorations(
       const NGTextFragmentPaintInfo& fragment_paint_info,
       const TextDecorationOffsetBase& decoration_offset,
       TextDecorationInfo& decoration_info,
@@ -59,6 +59,12 @@
       const TextPaintStyle& text_style,
       const cc::PaintFlags* flags = nullptr);
 
+  virtual void ClipDecorationsStripe(const NGTextFragmentPaintInfo&,
+                                     float upper,
+                                     float stripe_width,
+                                     float dilation) = 0;
+
+ private:
   void PaintDecorationUnderOrOverLine(
       const NGTextFragmentPaintInfo& fragment_paint_info,
       GraphicsContext& context,
@@ -66,10 +72,22 @@
       TextDecorationLine line,
       const cc::PaintFlags* flags = nullptr);
 
-  virtual void ClipDecorationsStripe(const NGTextFragmentPaintInfo&,
-                                     float upper,
-                                     float stripe_width,
-                                     float dilation) = 0;
+  void PaintUnderOrOverLineDecorationShadows(
+      const NGTextFragmentPaintInfo& fragment_paint_info,
+      const TextDecorationOffsetBase& decoration_offset,
+      TextDecorationInfo& decoration_info,
+      TextDecorationLine lines_to_paint,
+      const cc::PaintFlags* flags,
+      const TextPaintStyle& text_style,
+      GraphicsContext& context);
+
+  void PaintUnderOrOverLineDecorations(
+      const NGTextFragmentPaintInfo& fragment_paint_info,
+      const TextDecorationOffsetBase& decoration_offset,
+      TextDecorationInfo& decoration_info,
+      TextDecorationLine lines_to_paint,
+      const cc::PaintFlags* flags,
+      GraphicsContext& context);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index d4073c1d..1a54db9 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1643,7 +1643,7 @@
   if (local_transform_state && layout_object.StyleRef().BackfaceVisibility() ==
                                    EBackfaceVisibility::kHidden) {
     STACK_UNINITIALIZED TransformationMatrix inverted_matrix =
-        local_transform_state->AccumulatedTransform().Inverse();
+        local_transform_state->AccumulatedTransform().InverseOrIdentity();
     // If the z-vector of the matrix is negative, the back is facing towards the
     // viewer. TODO(crbug.com/1359528): Use something like
     // gfx::Transform::IsBackfaceVisible().
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 4f6937c..1157bdb 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -808,6 +808,7 @@
                   .FirstFragment()
                   .PaintProperties()
                   ->ScrollTranslation();
+      DCHECK(inner_most_scroll_container);
       scoped_refptr<const TransformPaintPropertyNode>
           outer_most_scroll_container =
               anchor_scroll_data.ScrollContainerLayers()
@@ -816,6 +817,7 @@
                   .FirstFragment()
                   .PaintProperties()
                   ->ScrollTranslation();
+      DCHECK(outer_most_scroll_container);
       state.anchor_scroll_containers_data = std::make_unique<
           TransformPaintPropertyNode::AnchorScrollContainersData>(
           std::move(inner_most_scroll_container),
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index f7dd7dfb..76ad462 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -1206,28 +1206,10 @@
   return false;
 }
 
-void ComputedStyle::AddCursor(StyleImage* image,
-                              bool hot_spot_specified,
-                              const gfx::Point& hot_spot) {
-  if (!CursorDataInternal())
-    SetCursorDataInternal(MakeGarbageCollected<CursorList>());
-  MutableCursorDataInternal()->push_back(
-      CursorData(image, hot_spot_specified, hot_spot));
-}
-
-void ComputedStyle::SetCursorList(CursorList* other) {
-  SetCursorDataInternal(other);
-}
-
 bool ComputedStyle::QuotesDataEquivalent(const ComputedStyle& other) const {
   return base::ValuesEquivalent(Quotes(), other.Quotes());
 }
 
-void ComputedStyle::ClearCursorList() {
-  if (CursorDataInternal())
-    SetCursorDataInternal(nullptr);
-}
-
 static bool HasPropertyThatCreatesStackingContext(
     const Vector<CSSPropertyID>& properties) {
   for (CSSPropertyID property : properties) {
@@ -1296,10 +1278,6 @@
     MutableCallbackSelectorsInternal().push_back(selector);
 }
 
-void ComputedStyle::SetContent(ContentData* content_data) {
-  SetContentInternal(content_data);
-}
-
 static bool IsWillChangeTransformHintProperty(CSSPropertyID property) {
   switch (ResolveCSSPropertyID(property)) {
     case CSSPropertyID::kTransform:
@@ -1561,13 +1539,6 @@
   return base::ValuesEquivalent(TextShadow(), other.TextShadow());
 }
 
-StyleImage* ComputedStyle::ListStyleImage() const {
-  return ListStyleImageInternal();
-}
-void ComputedStyle::SetListStyleImage(StyleImage* v) {
-  SetListStyleImageInternal(v);
-}
-
 bool ComputedStyle::SetEffectiveZoom(float f) {
   // Clamp the effective zoom value to a smaller (but hopeful still large
   // enough) range, to avoid overflow in derived computations.
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 673cd492..1fb2355 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -500,31 +500,18 @@
     DCHECK(BackdropFilterInternal().Get());
     return BackdropFilterInternal()->operations_;
   }
-  FilterOperations& MutableBackdropFilter() {
-    DCHECK(BackdropFilterInternal().Get());
-    return MutableBackdropFilterInternal()->operations_;
-  }
   // For containing blocks, use |HasNonInitialBackdropFilter()| which includes
   // will-change: backdrop-filter.
   bool HasBackdropFilter() const {
     DCHECK(BackdropFilterInternal().Get());
     return !BackdropFilterInternal()->operations_.Operations().empty();
   }
-  void SetBackdropFilter(const FilterOperations& ops) {
-    DCHECK(BackdropFilterInternal().Get());
-    if (BackdropFilterInternal()->operations_ != ops)
-      MutableBackdropFilterInternal()->operations_ = ops;
-  }
   bool BackdropFilterDataEquivalent(const ComputedStyle& o) const {
     return base::ValuesEquivalent(BackdropFilterInternal(),
                                   o.BackdropFilterInternal());
   }
 
   // filter (aka -webkit-filter)
-  FilterOperations& MutableFilter() {
-    DCHECK(FilterInternal().Get());
-    return MutableFilterInternal()->operations_;
-  }
   const FilterOperations& Filter() const {
     DCHECK(FilterInternal().Get());
     return FilterInternal()->operations_;
@@ -535,11 +522,6 @@
     DCHECK(FilterInternal().Get());
     return !FilterInternal()->operations_.Operations().empty();
   }
-  void SetFilter(const FilterOperations& v) {
-    DCHECK(FilterInternal().Get());
-    if (FilterInternal()->operations_ != v)
-      MutableFilterInternal()->operations_ = v;
-  }
   bool FilterDataEquivalent(const ComputedStyle& o) const {
     return base::ValuesEquivalent(FilterInternal(), o.FilterInternal());
   }
@@ -633,20 +615,6 @@
     // in more easily accessible memory.
     return HasClipPath() ? ClipPathInternal().get() : nullptr;
   }
-  void SetClipPath(scoped_refptr<ClipPathOperation> clip_path) {
-    SetHasClipPath(clip_path.get());
-    SetClipPathInternal(std::move(clip_path));
-  }
-
-  // clip
-  void SetClip(const LengthBox& box) {
-    SetHasAutoClipInternal(false);
-    SetClipInternal(box);
-  }
-  void SetHasAutoClip() {
-    SetHasAutoClipInternal(true);
-    SetClipInternal(ComputedStyleInitialValues::InitialClip());
-  }
 
   // column-rule-width
   uint16_t ColumnRuleWidth() const {
@@ -658,7 +626,6 @@
 
   // content
   ContentData* GetContentData() const { return ContentInternal().Get(); }
-  void SetContent(ContentData*);
 
   // -webkit-line-clamp
   bool HasLineClamp() const { return LineClamp() > 0; }
@@ -838,9 +805,6 @@
   Length LineHeight() const;
 
   // List style properties.
-  // list-style-image
-  CORE_EXPORT StyleImage* ListStyleImage() const;
-  void SetListStyleImage(StyleImage*);
 
   // list-style-type
   const AtomicString& ListStyleStringValue() const;
@@ -1247,21 +1211,6 @@
   const Length& LogicalHeight() const {
     return IsHorizontalWritingMode() ? Height() : Width();
   }
-  void SetLogicalWidth(const Length& v) {
-    if (IsHorizontalWritingMode()) {
-      SetWidth(v);
-    } else {
-      SetHeight(v);
-    }
-  }
-
-  void SetLogicalHeight(const Length& v) {
-    if (IsHorizontalWritingMode()) {
-      SetHeight(v);
-    } else {
-      SetWidth(v);
-    }
-  }
   const Length& LogicalMaxWidth() const {
     return IsHorizontalWritingMode() ? MaxWidth() : MaxHeight();
   }
@@ -1825,11 +1774,6 @@
 
   // Cursor utility functions.
   CursorList* Cursors() const { return CursorDataInternal().Get(); }
-  CORE_EXPORT void AddCursor(StyleImage*,
-                             bool hot_spot_specified,
-                             const gfx::Point& hot_spot = gfx::Point());
-  void SetCursorList(CursorList*);
-  void ClearCursorList();
 
   // Resize utility functions.
   bool HasResize() const {
@@ -2850,6 +2794,33 @@
   }
 #endif  // DCHECK_IS_ON()
 
+  // backdrop-filter
+  FilterOperations& MutableBackdropFilter() {
+    DCHECK(BackdropFilterInternal().Get());
+    return MutableBackdropFilterInternal()->operations_;
+  }
+  void SetBackdropFilter(const FilterOperations& ops) {
+    DCHECK(BackdropFilterInternal().Get());
+    if (BackdropFilterInternal()->operations_ != ops)
+      MutableBackdropFilterInternal()->operations_ = ops;
+  }
+
+  // clip
+  void SetClip(const LengthBox& box) {
+    SetHasAutoClipInternal(false);
+    SetClipInternal(box);
+  }
+  void SetHasAutoClip() {
+    SetHasAutoClipInternal(true);
+    SetClipInternal(ComputedStyleInitialValues::InitialClip());
+  }
+
+  // clip-patch
+  void SetClipPath(scoped_refptr<ClipPathOperation> clip_path) {
+    SetHasClipPath(clip_path.get());
+    SetClipPathInternal(std::move(clip_path));
+  }
+
   // column-count
   void SetColumnCount(uint16_t c) {
     SetHasAutoColumnCountInternal(false);
@@ -2875,6 +2846,32 @@
     SetColumnWidthInternal(0);
   }
 
+  // cursor
+  void AddCursor(StyleImage* image,
+                 bool hot_spot_specified,
+                 const gfx::Point& hot_spot = gfx::Point()) {
+    if (!CursorDataInternal())
+      SetCursorDataInternal(MakeGarbageCollected<CursorList>());
+    MutableCursorDataInternal()->push_back(
+        CursorData(image, hot_spot_specified, hot_spot));
+  }
+  void SetCursorList(CursorList* list) { SetCursorDataInternal(list); }
+  void ClearCursorList() {
+    if (CursorDataInternal())
+      SetCursorDataInternal(nullptr);
+  }
+
+  // filter
+  FilterOperations& MutableFilter() {
+    DCHECK(FilterInternal().Get());
+    return MutableFilterInternal()->operations_;
+  }
+  void SetFilter(const FilterOperations& v) {
+    DCHECK(FilterInternal().Get());
+    if (FilterInternal()->operations_ != v)
+      MutableFilterInternal()->operations_ = v;
+  }
+
   // margin-*
   void SetMarginTop(const Length& v) {
     if (MarginTop() != v) {
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index d3ff35e..792203549 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -65,11 +65,11 @@
 TEST_F(ComputedStyleTest, ShapeOutsideBoxEqual) {
   auto* shape1 = MakeGarbageCollected<ShapeValue>(CSSBoxType::kContent);
   auto* shape2 = MakeGarbageCollected<ShapeValue>(CSSBoxType::kContent);
-  scoped_refptr<ComputedStyle> style1 = CreateComputedStyle();
-  scoped_refptr<ComputedStyle> style2 = CreateComputedStyle();
-  style1->SetShapeOutside(shape1);
-  style2->SetShapeOutside(shape2);
-  EXPECT_EQ(*style1, *style2);
+  ComputedStyleBuilder builder1 = CreateComputedStyleBuilder();
+  ComputedStyleBuilder builder2 = CreateComputedStyleBuilder();
+  builder1.SetShapeOutside(shape1);
+  builder2.SetShapeOutside(shape2);
+  EXPECT_EQ(*builder1.TakeStyle(), *builder2.TakeStyle());
 }
 
 TEST_F(ComputedStyleTest, ShapeOutsideCircleEqual) {
@@ -79,11 +79,11 @@
                                                   CSSBoxType::kContent);
   auto* shape2 = MakeGarbageCollected<ShapeValue>(std::move(circle2),
                                                   CSSBoxType::kContent);
-  scoped_refptr<ComputedStyle> style1 = CreateComputedStyle();
-  scoped_refptr<ComputedStyle> style2 = CreateComputedStyle();
-  style1->SetShapeOutside(shape1);
-  style2->SetShapeOutside(shape2);
-  EXPECT_EQ(*style1, *style2);
+  ComputedStyleBuilder builder1 = CreateComputedStyleBuilder();
+  ComputedStyleBuilder builder2 = CreateComputedStyleBuilder();
+  builder1.SetShapeOutside(shape1);
+  builder2.SetShapeOutside(shape2);
+  EXPECT_EQ(*builder1.TakeStyle(), *builder2.TakeStyle());
 }
 
 TEST_F(ComputedStyleTest, ClipPathEqual) {
@@ -92,11 +92,11 @@
       ShapeClipPathOperation::Create(shape);
   scoped_refptr<ShapeClipPathOperation> path2 =
       ShapeClipPathOperation::Create(shape);
-  scoped_refptr<ComputedStyle> style1 = CreateComputedStyle();
-  scoped_refptr<ComputedStyle> style2 = CreateComputedStyle();
-  style1->SetClipPath(path1);
-  style2->SetClipPath(path2);
-  EXPECT_EQ(*style1, *style2);
+  ComputedStyleBuilder builder1 = CreateComputedStyleBuilder();
+  ComputedStyleBuilder builder2 = CreateComputedStyleBuilder();
+  builder1.SetClipPath(path1);
+  builder2.SetClipPath(path2);
+  EXPECT_EQ(*builder1.TakeStyle(), *builder2.TakeStyle());
 }
 
 TEST_F(ComputedStyleTest, SVGStackingContext) {
@@ -118,10 +118,14 @@
 }
 
 TEST_F(ComputedStyleTest, LayoutContainmentStackingContext) {
-  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
   EXPECT_FALSE(style->IsStackingContextWithoutContainment());
-  style->SetContain(kContainsLayout);
-  style->UpdateIsStackingContextWithoutContainment(false, false, false);
+
+  ComputedStyleBuilder builder(*style);
+  builder.SetContain(kContainsLayout);
+  builder.MutableInternalStyle()->UpdateIsStackingContextWithoutContainment(
+      false, false, false);
+  style = builder.TakeStyle();
   // Containment doesn't change IsStackingContextWithoutContainment
   EXPECT_FALSE(style->IsStackingContextWithoutContainment());
 }
@@ -287,11 +291,12 @@
 
 TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsContainsPaint) {
-  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
-  scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
-
+  scoped_refptr<const ComputedStyle> style = CreateComputedStyle();
+  ComputedStyleBuilder builder(*style);
   // This induces a flat used transform style.
-  other->SetContain(kContainsPaint);
+  builder.SetContain(kContainsPaint);
+  scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
+
   StyleDifference diff;
   style->UpdatePropertySpecificDifferences(*other, diff);
   EXPECT_TRUE(diff.CompositingReasonsChanged());
@@ -332,9 +337,6 @@
 }
 
 TEST_F(ComputedStyleTest, CursorList) {
-  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
-  scoped_refptr<ComputedStyle> other = CreateComputedStyle();
-
   auto* gradient = MakeGarbageCollected<cssvalue::CSSLinearGradientValue>(
       nullptr, nullptr, nullptr, nullptr, nullptr, cssvalue::kRepeating);
 
@@ -345,8 +347,13 @@
 
   EXPECT_TRUE(base::ValuesEquivalent(image_value, other_image_value));
 
-  style->AddCursor(image_value, false);
-  other->AddCursor(other_image_value, false);
+  ComputedStyleBuilder builder = CreateComputedStyleBuilder();
+  builder.AddCursor(image_value, false);
+  scoped_refptr<const ComputedStyle> style = builder.TakeStyle();
+
+  builder = CreateComputedStyleBuilder();
+  builder.AddCursor(other_image_value, false);
+  scoped_refptr<const ComputedStyle> other = builder.TakeStyle();
   EXPECT_EQ(*style, *other);
 }
 
@@ -1500,11 +1507,14 @@
 TEST_F(ComputedStyleTest, DebugDiffFields) {
   using DebugField = ComputedStyleBase::DebugField;
 
-  scoped_refptr<ComputedStyle> style1 = CreateComputedStyle();
-  scoped_refptr<ComputedStyle> style2 = CreateComputedStyle();
+  ComputedStyleBuilder builder1 = CreateComputedStyleBuilder();
+  ComputedStyleBuilder builder2 = CreateComputedStyleBuilder();
 
-  style1->SetWidth(Length(100.0, Length::kFixed));
-  style2->SetWidth(Length(200.0, Length::kFixed));
+  builder1.SetWidth(Length(100.0, Length::kFixed));
+  builder2.SetWidth(Length(200.0, Length::kFixed));
+
+  scoped_refptr<const ComputedStyle> style1 = builder1.TakeStyle();
+  scoped_refptr<const ComputedStyle> style2 = builder2.TakeStyle();
 
   EXPECT_EQ(0u, style1->DebugDiffFields(*style1).size());
   EXPECT_EQ(0u, style2->DebugDiffFields(*style2).size());
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index a44d76ce0..9c3b55a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -3973,12 +3973,6 @@
   bool already_visited = visited.Contains(this);
   visited.insert(this);
 
-  // Slots are elements that cannot be named.
-  if (IsA<HTMLSlotElement>(GetNode())) {
-    *found_text_alternative = false;
-    return String();
-  }
-
   // Step 2A from: http://www.w3.org/TR/accname-aam-1.1
   // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
   if (IsHiddenForTextAlternativeCalculation(aria_label_or_description_root)) {
diff --git a/third_party/blink/renderer/modules/mediarecorder/DEPS b/third_party/blink/renderer/modules/mediarecorder/DEPS
index 38a0c54..81ff459c 100644
--- a/third_party/blink/renderer/modules/mediarecorder/DEPS
+++ b/third_party/blink/renderer/modules/mediarecorder/DEPS
@@ -19,7 +19,7 @@
     '+third_party/opus',
     "+third_party/libvpx",
     "+third_party/libyuv",
-    "+third_party/openh264/src/codec/api/svc",
+    "+third_party/openh264/src/codec/api/wels",
     "+ui/gfx/geometry",
 ]
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc
index 88e4877..423abec 100644
--- a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.cc
@@ -22,8 +22,8 @@
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
-#include "third_party/openh264/src/codec/api/svc/codec_app_def.h"
-#include "third_party/openh264/src/codec/api/svc/codec_def.h"
+#include "third_party/openh264/src/codec/api/wels/codec_app_def.h"
+#include "third_party/openh264/src/codec/api/wels/codec_def.h"
 #include "ui/gfx/geometry/size.h"
 
 using media::VideoFrame;
diff --git a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h
index 7eb18a1..6dadffc 100644
--- a/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/h264_encoder.h
@@ -14,7 +14,7 @@
 
 #include "base/time/time.h"
 #include "third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h"
-#include "third_party/openh264/src/codec/api/svc/codec_api.h"
+#include "third_party/openh264/src/codec/api/wels/codec_api.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/webaudio/BUILD.gn b/third_party/blink/renderer/modules/webaudio/BUILD.gn
index 490c4b4..382234f 100644
--- a/third_party/blink/renderer/modules/webaudio/BUILD.gn
+++ b/third_party/blink/renderer/modules/webaudio/BUILD.gn
@@ -58,6 +58,8 @@
     "audio_scheduled_source_handler.h",
     "audio_scheduled_source_node.cc",
     "audio_scheduled_source_node.h",
+    "audio_sink_info.cc",
+    "audio_sink_info.h",
     "audio_summing_junction.cc",
     "audio_summing_junction.h",
     "audio_worklet.cc",
diff --git a/third_party/blink/renderer/modules/webaudio/audio_sink_info.cc b/third_party/blink/renderer/modules/webaudio/audio_sink_info.cc
new file mode 100644
index 0000000..60e86905
--- /dev/null
+++ b/third_party/blink/renderer/modules/webaudio/audio_sink_info.cc
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webaudio/audio_sink_info.h"
+
+namespace blink {
+
+AudioSinkInfo* AudioSinkInfo::Create(const String& type) {
+  return MakeGarbageCollected<AudioSinkInfo>(type);
+}
+
+AudioSinkInfo::AudioSinkInfo(const String& type) {}
+
+AudioSinkInfo::~AudioSinkInfo() = default;
+
+String AudioSinkInfo::type() const {
+  // Currently "none" is the only `type` available.
+  return "none";
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_sink_info.h b/third_party/blink/renderer/modules/webaudio/audio_sink_info.h
new file mode 100644
index 0000000..e6a953722
--- /dev/null
+++ b/third_party/blink/renderer/modules/webaudio/audio_sink_info.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_SINK_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_SINK_INFO_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class AudioSinkInfo : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static AudioSinkInfo* Create(const String&);
+
+  explicit AudioSinkInfo(const String&);
+  ~AudioSinkInfo() override;
+
+  String type() const;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_SINK_INFO_H_
diff --git a/third_party/blink/renderer/modules/webaudio/audio_sink_info.idl b/third_party/blink/renderer/modules/webaudio/audio_sink_info.idl
new file mode 100644
index 0000000..2fa736e0
--- /dev/null
+++ b/third_party/blink/renderer/modules/webaudio/audio_sink_info.idl
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+enum AudioSinkType {
+  "none"
+};
+
+[Exposed=Window, RuntimeEnabled=AudioContextSetSinkId] interface AudioSinkInfo {
+  readonly attribute AudioSinkType type;
+};
diff --git a/third_party/blink/renderer/modules/xr/xr_frame.cc b/third_party/blink/renderer/modules/xr/xr_frame.cc
index 99f79d1..9d5cf2b 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame.cc
@@ -392,11 +392,8 @@
     return {};
   }
 
-  const TransformationMatrix& mojo_from_stationary_space =
-      reference_space_information->mojo_from_space;
-
-  DCHECK(mojo_from_stationary_space.IsInvertible());
-  auto stationary_space_from_mojo = mojo_from_stationary_space.Inverse();
+  auto stationary_space_from_mojo =
+      reference_space_information->mojo_from_space.GetCheckedInverse();
 
   // We now have 2 spaces - the dynamic one passed in to create anchor
   // call, and the stationary one. We also have a rigid transform
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc b/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc
index da8736c..0dea09d 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_result.cc
@@ -80,12 +80,8 @@
     return {};
   }
 
-  const TransformationMatrix& mojo_from_space =
-      reference_space_information->mojo_from_space;
-
-  DCHECK(mojo_from_space.IsInvertible());
-
-  auto space_from_mojo = mojo_from_space.Inverse();
+  auto space_from_mojo =
+      reference_space_information->mojo_from_space.GetCheckedInverse();
   auto space_from_anchor =
       space_from_mojo * TransformationMatrix(mojo_from_this_.ToTransform());
 
diff --git a/third_party/blink/renderer/modules/xr/xr_space.cc b/third_party/blink/renderer/modules/xr/xr_space.cc
index 47b9aae..93a23757 100644
--- a/third_party/blink/renderer/modules/xr/xr_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_space.cc
@@ -58,8 +58,7 @@
   if (!mojo_from_native)
     return absl::nullopt;
 
-  DCHECK(mojo_from_native->IsInvertible());
-  return mojo_from_native->Inverse();
+  return mojo_from_native->GetCheckedInverse();
 }
 
 bool XRSpace::EmulatedPosition() const {
diff --git a/third_party/blink/renderer/modules/xr/xr_view.cc b/third_party/blink/renderer/modules/xr/xr_view.cc
index cb1dce9..09449cf 100644
--- a/third_party/blink/renderer/modules/xr/xr_view.cc
+++ b/third_party/blink/renderer/modules/xr/xr_view.cc
@@ -148,7 +148,7 @@
                                                   double canvas_height) {
   // Recompute the inverse projection matrix if needed.
   if (inv_projection_dirty_) {
-    inv_projection_ = projection_matrix_.Inverse();
+    inv_projection_ = projection_matrix_.InverseOrIdentity();
     inv_projection_dirty_ = false;
   }
 
@@ -183,7 +183,7 @@
                           -point_in_view_space.z());
 
   // LookAt matrices are view matrices (inverted), so invert before returning.
-  return inv_pointer.Inverse();
+  return inv_pointer.InverseOrIdentity();
 }
 
 XRRigidTransform* XRView::refSpaceFromView() const {
diff --git a/third_party/blink/renderer/platform/graphics/color.h b/third_party/blink/renderer/platform/graphics/color.h
index 00978ee6..6bc8bf16 100644
--- a/third_party/blink/renderer/platform/graphics/color.h
+++ b/third_party/blink/renderer/platform/graphics/color.h
@@ -252,9 +252,12 @@
   bool SetFromString(const String&);
   bool SetNamedColor(const String&);
 
-  // Return true if the color is not opaque.
+  // Returns true if the color is not opaque.
   bool HasAlpha() const { return Alpha() < 255; }
 
+  // Returns true if the color is transparent.
+  bool IsTransparent() const { return Alpha() == 0; }
+
   // Access the color as though it were created using rgba syntax. This will
   // clamp all colors to an 8-bit sRGB representation. All callers of these
   // functions should be audited. The function Rgb(), despite the name, does
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
index 143da7c2..6d6a715 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
@@ -425,7 +425,7 @@
                    cc::PaintOpType::Restore}));                     // </t1>
   EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 2);
   EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1);
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4);
+  EXPECT_TRANSFORM_MATRIX(t1->Matrix().GetCheckedInverse(), *output, 4);
 }
 
 TEST_P(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) {
@@ -459,7 +459,7 @@
            cc::PaintOpType::Restore}));                     // </t1>
   EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1);
   EXPECT_EFFECT_BOUNDS(0, 0, 50, 50, *output, 2);
-  EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4);
+  EXPECT_TRANSFORM_MATRIX(t1->Matrix().GetCheckedInverse(), *output, 4);
 }
 
 TEST_P(PaintChunksToCcLayerTest, NonRootLayerSimple) {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 55358e65..f0fbef6 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -250,9 +250,7 @@
   gfx::Transform to_screen;
   to_screen.Scale(device_scale_factor, device_scale_factor);
   transform_tree_.SetToScreen(cc::kRootPropertyNodeId, to_screen);
-  gfx::Transform from_screen;
-  bool invertible = to_screen.GetInverse(&from_screen);
-  DCHECK(invertible);
+  gfx::Transform from_screen = to_screen.GetCheckedInverse();
   transform_tree_.SetFromScreen(cc::kRootPropertyNodeId, from_screen);
   transform_tree_.set_needs_update(true);
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
index d82ad98..6949832 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
@@ -104,7 +104,7 @@
       plane_root_transform_->to_plane_root.MakeIdentity();
       parent.ApplyToPlaneRoot(plane_root_transform_->to_plane_root);
       plane_root_transform_->to_plane_root.PreConcat(local);
-      plane_root_transform_->from_plane_root = local.Inverse();
+      plane_root_transform_->from_plane_root = local.GetCheckedInverse();
       parent.ApplyFromPlaneRoot(plane_root_transform_->from_plane_root);
       plane_root_transform_->has_animation =
           parent.has_animation_to_plane_root() ||
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc
index fa50686..01d2da2 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc
@@ -67,14 +67,14 @@
 
     EXPECT_EQ(&t0(), cache.plane_root());
     EXPECT_EQ(to_plane_root, cache.to_plane_root());
-    EXPECT_EQ(to_plane_root.Inverse(), cache.from_plane_root());
+    EXPECT_EQ(to_plane_root.InverseOrIdentity(), cache.from_plane_root());
 
     TransformationMatrix actual_to_screen;
     cache.ApplyToScreen(actual_to_screen);
     EXPECT_EQ(to_plane_root, actual_to_screen);
     TransformationMatrix actual_projection_from_screen;
     cache.ApplyProjectionFromScreen(actual_projection_from_screen);
-    EXPECT_EQ(to_plane_root.Inverse(), actual_projection_from_screen);
+    EXPECT_EQ(to_plane_root.InverseOrIdentity(), actual_projection_from_screen);
   }
 
   static void CheckPlaneRootSameAs2dTranslationRoot(
@@ -97,7 +97,7 @@
     EXPECT_EQ(to_screen, cache.to_screen());
     auto projection_from_screen = to_screen;
     projection_from_screen.FlattenTo2d();
-    projection_from_screen = projection_from_screen.Inverse();
+    projection_from_screen = projection_from_screen.InverseOrIdentity();
     EXPECT_EQ(projection_from_screen, cache.projection_from_screen());
   }
 
@@ -121,11 +121,11 @@
 
     EXPECT_EQ(&plane_root, cache.plane_root());
     EXPECT_EQ(to_plane_root, cache.to_plane_root());
-    EXPECT_EQ(to_plane_root.Inverse(), cache.from_plane_root());
+    EXPECT_EQ(to_plane_root.InverseOrIdentity(), cache.from_plane_root());
     EXPECT_EQ(to_screen, cache.to_screen());
     auto projection_from_screen = to_screen;
     projection_from_screen.FlattenTo2d();
-    projection_from_screen = projection_from_screen.Inverse();
+    projection_from_screen = projection_from_screen.InverseOrIdentity();
     EXPECT_EQ(projection_from_screen, cache.projection_from_screen());
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 84995f4..d59b560 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -140,7 +140,8 @@
       if (other.IsIdentityOr2DTranslation())
         return Matrix().Preserves2dAxisAlignment();
       // TODO(crbug.com/960481): Consider more rare corner cases.
-      return (Matrix().Inverse() * other.Matrix()).Preserves2dAxisAlignment();
+      return (Matrix().InverseOrIdentity() * other.Matrix())
+          .Preserves2dAxisAlignment();
     }
 
    private:
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 ed2a0845..000c6017 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
@@ -939,8 +939,9 @@
         &WebMediaPlayerImpl::OnDataSourceRedirected, weak_this_));
     mb_data_source_->SetPreload(preload_);
     mb_data_source_->SetIsClientAudioElement(client_->IsAudioElement());
-    mb_data_source_->Initialize(
-        base::BindOnce(&WebMediaPlayerImpl::DataSourceInitialized, weak_this_));
+
+    mb_data_source_->Initialize(base::BindOnce(
+        &WebMediaPlayerImpl::MultiBufferDataSourceInitialized, weak_this_));
   }
 }
 
@@ -1941,7 +1942,7 @@
                                           .SchemeIsCryptographic();
     bool manifest_url_is_cryptographic =
         loaded_url_.SchemeIsCryptographic() &&
-        mb_data_source_->GetUrlAfterRedirects().SchemeIsCryptographic();
+        data_source_->GetUrlAfterRedirects().SchemeIsCryptographic();
     UMA_HISTOGRAM_BOOLEAN(
         "Media.WebMediaPlayerImpl.HLS.IsMixedContent",
         frame_url_is_cryptographic && !manifest_url_is_cryptographic);
@@ -1949,7 +1950,7 @@
     renderer_factory_selector_->SetBaseRendererType(
         media::RendererType::kMediaPlayer);
 
-    loaded_url_ = mb_data_source_->GetUrlAfterRedirects();
+    loaded_url_ = data_source_->GetUrlAfterRedirects();
     DCHECK(data_source_);
     data_source_->Stop();
     mb_data_source_ = nullptr;
@@ -2749,9 +2750,6 @@
   DVLOG(1) << __func__;
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
-  if (observer_ && mb_data_source_)
-    observer_->OnDataSourceInitialized(mb_data_source_->GetUrlAfterRedirects());
-
   if (!success) {
     SetNetworkState(WebMediaPlayer::kNetworkStateFormatError);
     media_metrics_provider_->OnError(media::PIPELINE_ERROR_NETWORK);
@@ -2763,13 +2761,21 @@
     return;
   }
 
-  // No point in preloading data as we'll probably just throw it away anyways.
-  if (IsStreaming() && preload_ > media::DataSource::METADATA)
-    data_source_->SetPreload(media::DataSource::METADATA);
-
   StartPipeline();
 }
 
+void WebMediaPlayerImpl::MultiBufferDataSourceInitialized(bool success) {
+  DVLOG(1) << __func__;
+  DCHECK(data_source_);
+  if (observer_)
+    observer_->OnDataSourceInitialized(data_source_->GetUrlAfterRedirects());
+
+  // No point in preloading data as we'll probably just throw it away anyways.
+  if (success && IsStreaming() && preload_ > media::DataSource::METADATA)
+    data_source_->SetPreload(media::DataSource::METADATA);
+  DataSourceInitialized(success);
+}
+
 void WebMediaPlayerImpl::OnDataSourceRedirected() {
   DVLOG(1) << __func__;
   DCHECK(main_task_runner_->BelongsToCurrentThread());
@@ -3985,7 +3991,7 @@
 }
 
 GURL WebMediaPlayerImpl::GetSrcAfterRedirects() {
-  return mb_data_source_ ? mb_data_source_->GetUrlAfterRedirects() : GURL();
+  return data_source_ ? data_source_->GetUrlAfterRedirects() : GURL();
 }
 
 void WebMediaPlayerImpl::UpdateSmoothnessHelper() {
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.h b/third_party/blink/renderer/platform/media/web_media_player_impl.h
index b6e77d9..9f40456a 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.h
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.h
@@ -392,9 +392,13 @@
               CorsMode cors_mode,
               bool is_cache_disabled);
 
-  // Called after asynchronous initialization of a data source completed.
+  // Called after synchronous initialization of a data source completes.
   void DataSourceInitialized(bool success);
 
+  // Called after asynchronous initialization of a multibuffer data source
+  // completes.
+  void MultiBufferDataSourceInitialized(bool success);
+
   // Called if the |MultiBufferDataSource| is redirected.
   void OnDataSourceRedirected();
 
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix.cc b/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
index 05e0a3e..3570976 100644
--- a/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
+++ b/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
@@ -691,7 +691,7 @@
   return InternalInverse<true>(nullptr);
 }
 
-TransformationMatrix TransformationMatrix::Inverse() const {
+TransformationMatrix TransformationMatrix::InverseOrIdentity() const {
   TransformationMatrix m;
   InternalInverse<false>(&m);
   return m;
@@ -706,6 +706,13 @@
   return false;
 }
 
+TransformationMatrix TransformationMatrix::GetCheckedInverse() const {
+  TransformationMatrix m;
+  bool invertible = InternalInverse<false>(&m);
+  DCHECK(invertible);
+  return m;
+}
+
 template <bool check_invertibility_only>
 bool TransformationMatrix::InternalInverse(TransformationMatrix* result) const {
   DCHECK_EQ(check_invertibility_only, !result);
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix.h b/third_party/blink/renderer/platform/transforms/transformation_matrix.h
index d3c4eef..6de9c5c 100644
--- a/third_party/blink/renderer/platform/transforms/transformation_matrix.h
+++ b/third_party/blink/renderer/platform/transforms/transformation_matrix.h
@@ -280,7 +280,7 @@
 
   // This method returns the identity matrix if it is not invertible.
   // Use GetInverse() if you also need to know the invertibility.
-  [[nodiscard]] TransformationMatrix Inverse() const;
+  [[nodiscard]] TransformationMatrix InverseOrIdentity() const;
 
   // If this matrix is invertible, this method sets |result| to the inverse of
   // this matrix and returns true, otherwise sets |result| to identity and
@@ -288,6 +288,9 @@
   // consistent with gfx::Transform::GetInverse()).
   [[nodiscard]] bool GetInverse(TransformationMatrix* result) const;
 
+  // Same as above except that it assumes success, otherwise DCHECK fails.
+  [[nodiscard]] TransformationMatrix GetCheckedInverse() const;
+
   // decompose the matrix into its component parts
   typedef struct {
     double scale_x, scale_y, scale_z;
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc b/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc
index eb04062..1b30343 100644
--- a/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc
+++ b/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc
@@ -473,50 +473,6 @@
             column_major_constructor.ToString());
 }
 
-TEST(TransformationMatrix, IsInvertible) {
-  EXPECT_FALSE(TransformationMatrix::ColMajor(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                              0, 0, 0, 0, 0)
-                   .IsInvertible());
-  EXPECT_TRUE(TransformationMatrix().IsInvertible());
-  TransformationMatrix t;
-  t.Translate3d(10, 20, 30);
-  EXPECT_TRUE(t.IsInvertible());
-  EXPECT_TRUE(MakeScaleMatrix(1e-8).IsInvertible());
-  EXPECT_FALSE(MakeScaleMatrix(0).IsInvertible());
-  EXPECT_FALSE(
-      MakeScaleMatrix(std::numeric_limits<double>::quiet_NaN()).IsInvertible());
-  EXPECT_FALSE(
-      MakeScaleMatrix(std::numeric_limits<double>::min()).IsInvertible());
-}
-
-TEST(TransformationMatrix, Inverse) {
-  EXPECT_EQ(TransformationMatrix(), MakeScaleMatrix(0).Inverse());
-  EXPECT_EQ(TransformationMatrix(), TransformationMatrix().Inverse());
-
-  auto t1 = MakeTranslationMatrix(-10, 20, -30);
-  auto t2 = MakeTranslationMatrix(10, -20, 30);
-  EXPECT_EQ(t1, t2.Inverse());
-  EXPECT_EQ(t2, t1.Inverse());
-
-  auto s1 = MakeScaleMatrix(2, -4, 0.5);
-  auto s2 = MakeScaleMatrix(0.5, -0.25, 2);
-  EXPECT_EQ(s1, s2.Inverse());
-  EXPECT_EQ(s2, s1.Inverse());
-
-  TransformationMatrix m1;
-  m1.RotateAboutZAxis(-30);
-  m1.RotateAboutYAxis(10);
-  m1.RotateAboutXAxis(20);
-  m1.ApplyPerspectiveDepth(100);
-  TransformationMatrix m2;
-  m2.ApplyPerspectiveDepth(-100);
-  m2.RotateAboutXAxis(-20);
-  m2.RotateAboutYAxis(-10);
-  m2.RotateAboutZAxis(30);
-  EXPECT_TRANSFORMATION_MATRIX(m1, m2.Inverse());
-  EXPECT_TRANSFORMATION_MATRIX(m2, m1.Inverse());
-}
-
 TEST(TransformationMatrixTest, Blend2dXFlipTest) {
   // Test 2D x-flip (crbug.com/797472).
   auto from = TransformationMatrix::Affine(1, 0, 0, 1, 100, 150);
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
index e9e94f4..f1c35a1 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
@@ -184,6 +184,10 @@
             results = json.load(results_file)
 
         metadata = results.get('metadata') or {}
+        if 'num_failures_by_type' in results and 'PASS' in results[
+                'num_failures_by_type']:
+            num_passes = results['num_failures_by_type']['PASS']
+            results['num_passes'] = num_passes
         test_names = self._extract_artifacts(
             results['tests'],
             delim=results['path_delimiter'],
diff --git a/third_party/blink/tools/run_wpt_tests.py b/third_party/blink/tools/run_wpt_tests.py
index 4c55342..7530bf4 100755
--- a/third_party/blink/tools/run_wpt_tests.py
+++ b/third_party/blink/tools/run_wpt_tests.py
@@ -860,6 +860,9 @@
 
 
 def main():
+    # This environment fix is needed on windows as codec is trying
+    # to encode in cp1252 rather than utf-8 and throwing errors.
+    os.environ['PYTHONUTF8'] = '1'
     adapter = WPTAdapter()
     return adapter.run_test()
 
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
index de107b2..2bf79c2 100644
--- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
+++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -25,6 +25,7 @@
 svg/text/select-x-list-4.svg [ Crash ]
 # Unknown cause:
 crbug.com/1225856 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-display-none-editable.html [ Crash ]
+crbug.com/1380449 external/wpt/css/css-contain/content-visibility/content-visibility-063.html [ Crash ]
 
 # Failures
 # isUseCounted() difference:
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 1fd414c..3d741fe 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3383,7 +3383,7 @@
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-060.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-061.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-062.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-063.html [ Failure ]
+crbug.com/626703 crbug.com/1380449 external/wpt/css/css-contain/content-visibility/content-visibility-063.html [ Failure Crash ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-065.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-066.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/content-visibility/content-visibility-067.html [ Failure ]
@@ -3392,7 +3392,7 @@
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-060.html [ Failure ]
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-061.html [ Failure ]
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-062.html [ Failure ]
-crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-063.html [ Failure ]
+crbug.com/626703 crbug.com/1380449 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-063.html [ Failure Crash ]
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-065.html [ Failure ]
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-066.html [ Failure ]
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/css/css-contain/content-visibility/content-visibility-067.html [ Failure ]
@@ -7751,4 +7751,4 @@
 crbug.com/1376679 virtual/threaded/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html [ Skip ]
 
 # Sheriff 2022-11-01
-crbug.com/1380381 [ Linux ] external/wpt/css/css-ui/compute-kind-widget-no-fallback-props-001.html [ Failure Pass ]
\ No newline at end of file
+crbug.com/1380381 [ Linux ] external/wpt/css/css-ui/compute-kind-widget-no-fallback-props-001.html [ Failure Pass ]
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 1fdfeaf..9e45982 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
@@ -271940,6 +271940,10 @@
       "e67b97276818d50adb0e47105fbecab3c91e52c7",
       []
      ],
+     "import-conditions-expected.txt": [
+      "e1615689ea83cfadf93534f90e8f41460ebe7ea0",
+      []
+     ],
      "important-prop-ref.html": [
       "004679da7309ccc08640fe8c92b6f069090a37b6",
       []
@@ -343370,6 +343374,10 @@
       "3c072829e6f8cd5c2b94f12a8feeb5d860abc6cd",
       []
      ],
+     "scroll-timeline-dynamic.tentative-expected.txt": [
+      "57145213b5c3390eab9a2a397931d4505cf590f5",
+      []
+     ],
      "scroll-timeline-frame-size-changed-ref.html": [
       "ea7628ac723f4dea27bf5c60467a5896904930a3",
       []
@@ -394635,6 +394643,13 @@
        {}
       ]
      ],
+     "import-conditions.html": [
+      "d4a0918a5b245f12a28d6c96bd41a5b07bd29c09",
+      [
+       null,
+       {}
+      ]
+     ],
      "important-vs-inline-001.html": [
       "33b33bf943833ce2ea0f2c3e3ece08cf91189729",
       [
@@ -548251,7 +548266,7 @@
       ]
      ],
      "scroll-timeline-dynamic.tentative.html": [
-      "b78f15c0b7f8b457ae1866e346b022708a9b3443",
+      "b0880a7cc5ac8f831fdb1fd29fd477935f3a3fe9",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-fixedpos.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-fixedpos.tentative.html
new file mode 100644
index 0000000..ee7d2260
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-fixedpos.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>Tests that anchor-scroll adjusts location of fixed-positioned elements correctly</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/">
+<link rel="match" href="reference/anchor-scroll-fixedpos-ref.html">
+<style>
+body {
+  margin: 0;
+  height: 2000px;
+}
+
+div {
+  width: 100px;
+  height: 100px;
+}
+
+#anchor {
+  anchor-name: --a1;
+  margin: 300px;
+  background: orange;
+}
+
+#anchored {
+  position: fixed;
+  anchor-scroll: --a1;
+  left: anchor(--a1 right);
+  top: anchor(--a1 top);
+  background: green;
+}
+
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+document.documentElement.scrollTop = 200;
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-no-overflow-crash.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-no-overflow-crash.html
new file mode 100644
index 0000000..d8fa382
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-no-overflow-crash.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Tests that anchor-scroll doesn't crash renderer when anchor is in a scroller whose content doesn't overflow</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/">
+<style>
+#container {
+  position: relative;
+}
+
+#scroller {
+  overflow: scroll;
+  width: 400px;
+  height: 400px;
+}
+
+#anchor {
+  anchor-name: --a;
+  margin: 100px;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+
+#anchored {
+  position: absolute;
+  anchor-scroll: --a;
+  left: anchor(--a left);
+  bottom: anchor(--a top);
+  width: 100px;
+  height: 100px;
+  background: orange;
+}
+
+</style>
+
+<div id="container">
+  <div id="scroller">
+    <div id="anchor">anchor</div>
+  </div>
+  <div id="anchored">anchored</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html
new file mode 100644
index 0000000..e73354d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<style>
+body {
+  margin: 0;
+  height: 2000px;
+}
+
+div {
+  width: 100px;
+  height: 100px;
+}
+
+#anchor {
+  margin: 300px;
+  background: orange;
+}
+
+#anchored {
+  position: fixed;
+  left: 400px;
+  top: 100px;
+  background: green;
+}
+
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+document.documentElement.scrollTop = 200;
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative-expected.txt
new file mode 100644
index 0000000..5714521
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS Switching between document and scroll timelines [immediate]
+PASS Switching between document and scroll timelines [scroll]
+PASS Switching pending animation from document to scroll timelines [immediate]
+PASS Switching pending animation from document to scroll timelines [scroll]
+PASS Changing computed value of animation-timeline changes effective timeline [immediate]
+PASS Changing computed value of animation-timeline changes effective timeline [scroll]
+FAIL Changing to/from animation-timeline:none [immediate] assert_equals: expected "120px" but got "0px"
+FAIL Changing to/from animation-timeline:none [scroll] assert_equals: expected "120px" but got "0px"
+PASS Changing scroll-timeline on preceding elements affects target element [immediate]
+PASS Changing scroll-timeline on preceding elements affects target element [scroll]
+PASS Reverse animation direction [immediate]
+PASS Reverse animation direction [scroll]
+PASS Switching timelines while paused [immediate]
+PASS Switching timelines while paused [scroll]
+PASS Switching timelines and pausing at the same time [immediate]
+PASS Switching timelines and pausing at the same time [scroll]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html
index b78f15c..b0880a7c 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html
@@ -1,4 +1,5 @@
 <!DOCTYPE html>
+<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -172,7 +173,7 @@
 
     // ScrollTimeline -> none
     element.style.animationTimeline = 'none';
-    await assert_width(element, '0px');
+    await assert_width(element, '120px');
 
     // none -> ScrollTimeline
     element.style.animationTimeline = 'timeline';
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/shadow-multiple-expected.png
new file mode 100644
index 0000000..98c30647
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/text-antialias/stroking-decorations-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/text-antialias/stroking-decorations-expected.png
new file mode 100644
index 0000000..1ce47d45
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/text-antialias/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/fast/writing-mode/english-lr-text-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/fast/writing-mode/english-lr-text-expected.png
new file mode 100644
index 0000000..5a864bc
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/fast/writing-mode/english-lr-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/writing-mode/english-lr-text-expected.png b/third_party/blink/web_tests/platform/linux/fast/writing-mode/english-lr-text-expected.png
index 5a864bc..963778b 100644
--- a/third_party/blink/web_tests/platform/linux/fast/writing-mode/english-lr-text-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/writing-mode/english-lr-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/ietestcenter/css3/text/textshadow-002-expected.png b/third_party/blink/web_tests/platform/linux/ietestcenter/css3/text/textshadow-002-expected.png
index cbbf441..ce9e65e 100644
--- a/third_party/blink/web_tests/platform/linux/ietestcenter/css3/text/textshadow-002-expected.png
+++ b/third_party/blink/web_tests/platform/linux/ietestcenter/css3/text/textshadow-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/shadow-multiple-expected.png
index 98c30647..cabdfa8f 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/stroking-decorations-expected.png
index 1ce47d45..ed35105 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/stroking-decorations-expected.png
index ff1e445e..8a20ce0 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/paint/invalidation/shadow-multiple-expected.png
new file mode 100644
index 0000000..575d2b5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/paint/invalidation/shadow-multiple-expected.png
new file mode 100644
index 0000000..575d2b5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/writing-mode/english-lr-text-expected.png b/third_party/blink/web_tests/platform/mac/fast/writing-mode/english-lr-text-expected.png
index 2c1a028..2035c2b 100644
--- a/third_party/blink/web_tests/platform/mac/fast/writing-mode/english-lr-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/writing-mode/english-lr-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/ietestcenter/css3/text/textshadow-002-expected.png b/third_party/blink/web_tests/platform/mac/ietestcenter/css3/text/textshadow-002-expected.png
index 44ec0ed..f5d1059 100644
--- a/third_party/blink/web_tests/platform/mac/ietestcenter/css3/text/textshadow-002-expected.png
+++ b/third_party/blink/web_tests/platform/mac/ietestcenter/css3/text/textshadow-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/shadow-multiple-expected.png
index 44789bb7..4876e755b3 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/stroking-decorations-expected.png
index 70c851f6..d6589be3 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/writing-mode/english-lr-text-expected.png b/third_party/blink/web_tests/platform/win/fast/writing-mode/english-lr-text-expected.png
index cf160afd..53ed456 100644
--- a/third_party/blink/web_tests/platform/win/fast/writing-mode/english-lr-text-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/writing-mode/english-lr-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/ietestcenter/css3/text/textshadow-002-expected.png b/third_party/blink/web_tests/platform/win/ietestcenter/css3/text/textshadow-002-expected.png
index f8aa958..57ddf1d 100644
--- a/third_party/blink/web_tests/platform/win/ietestcenter/css3/text/textshadow-002-expected.png
+++ b/third_party/blink/web_tests/platform/win/ietestcenter/css3/text/textshadow-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/shadow-multiple-expected.png
index 16410b0..fa2baf5 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/stroking-decorations-expected.png
index a60df6b1..85afa7d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index c12858e..88f030f 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -398,6 +398,10 @@
     method start
     method stop
     setter onended
+interface AudioSinkInfo
+    attribute @@toStringTag
+    getter type
+    method constructor
 interface AudioTrack
     attribute @@toStringTag
     getter enabled
diff --git a/third_party/openh264/BUILD.gn b/third_party/openh264/BUILD.gn
index 421f98cc..e5e71a7 100644
--- a/third_party/openh264/BUILD.gn
+++ b/third_party/openh264/BUILD.gn
@@ -12,7 +12,7 @@
 buildflag_header("buildflags") {
   header = "buildflags.h"
   header_dir = "third_party/openh264"
-  flags = [ "OPENH264_API_WELS=0" ]
+  flags = [ "OPENH264_API_WELS=1" ]
 }
 
 # Config shared by all openh264 targets.
diff --git a/third_party/openh264/README.chromium b/third_party/openh264/README.chromium
index 599e7d4f..a877a92 100644
--- a/third_party/openh264/README.chromium
+++ b/third_party/openh264/README.chromium
@@ -3,7 +3,7 @@
 URL: http://www.openh264.org/
 Version: unknown
 CPEPrefix: cpe:/a:cisco:openh264:2.1.1
-(Cut at 3dd5b80bc4f172dd82925bb259cb7c82348409c5, which is 2.1.1)
+(Cut at db956674bbdfbaab5acdd3fdb4117c2fef5527e9, which is 2.3.0)
 License: 2-Clause BSD
 License File: src/LICENSE
 Security Critical: yes
diff --git a/third_party/openh264/openh264_sources.gni b/third_party/openh264/openh264_sources.gni
index e714dad9..01d06eb 100644
--- a/third_party/openh264/openh264_sources.gni
+++ b/third_party/openh264/openh264_sources.gni
@@ -1,6 +1,6 @@
 # Common
 openh264_common_include_dirs = [
-  "//third_party/openh264/src/codec/api/svc",
+  "//third_party/openh264/src/codec/api/wels",
   "//third_party/openh264/src/codec/common/arm",
   "//third_party/openh264/src/codec/common/inc",
   "//third_party/openh264/src/codec/common/src",
@@ -87,7 +87,7 @@
 
 # Processing
 openh264_processing_include_dirs = [
-  "//third_party/openh264/src/codec/api/svc",
+  "//third_party/openh264/src/codec/api/wels",
   "//third_party/openh264/src/codec/common/arm",
   "//third_party/openh264/src/codec/common/inc",
   "//third_party/openh264/src/codec/common/src",
@@ -164,7 +164,7 @@
 
 # Encoder
 openh264_encoder_include_dirs = [
-  "//third_party/openh264/src/codec/api/svc",
+  "//third_party/openh264/src/codec/api/wels",
   "//third_party/openh264/src/codec/common/arm/",
   "//third_party/openh264/src/codec/common/inc",
   "//third_party/openh264/src/codec/common/src",
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index a63407b..828b87d 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1181,34 +1181,34 @@
       'linux-x64-castos': 'cast_release_trybot',
       'linux-x64-castos-audio': 'cast_audio_release_trybot',
       'linux-x64-castos-dbg': 'cast_debug_bot',
-      'linux_chromium_archive_rel_ng': 'release_bot',
+      'linux_chromium_archive_rel_ng': 'release_bot_reclient',
       'linux_chromium_asan_rel_ng': 'asan_lsan_release_trybot_minimum_symbols_reclient',
-      'linux_chromium_cfi_rel_ng': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_goma',
+      'linux_chromium_cfi_rel_ng': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient',
       'linux_chromium_chromeos_asan_rel_ng': 'asan_lsan_chromeos_release_bot_dcheck_always_on',
       'linux_chromium_chromeos_msan_focal': 'chromeos_msan_release_bot',
       'linux_chromium_chromeos_msan_rel_ng': 'chromeos_msan_release_bot',
       'linux_chromium_clobber_deterministic': 'release_trybot',
       'linux_chromium_clobber_rel_ng': 'release_trybot',
       'linux_chromium_compile_dbg_ng': 'debug_bot_reclient',
-      'linux_chromium_compile_rel_ng': 'release_trybot',
-      'linux_chromium_dbg_ng': 'gpu_tests_debug_bot',
+      'linux_chromium_compile_rel_ng': 'release_trybot_reclient',
+      'linux_chromium_dbg_ng': 'gpu_tests_debug_bot_reclient',
 
       # This is intentionally a release_bot and not a release_trybot;
       # enabling DCHECKs seems to cause flaky failures that don't show up
       # on the continuous builder.
-      'linux_chromium_msan_focal': 'msan_release_bot',
-      'linux_chromium_msan_rel_ng': 'msan_release_bot',
+      'linux_chromium_msan_focal': 'msan_release_bot_reclient',
+      'linux_chromium_msan_rel_ng': 'msan_release_bot_reclient',
 
       'linux_chromium_tsan_rel_ng': 'tsan_disable_nacl_release_trybot_reclient',
 
       # This is intentionally a release_bot and not a release_trybot to match
       # the CI configuration, where no debug builder exists.
-      'linux_chromium_ubsan_rel_ng': 'ubsan_vptr_release_bot',
+      'linux_chromium_ubsan_rel_ng': 'ubsan_vptr_release_bot_reclient',
 
       'linux_layout_tests_layout_ng_disabled': 'release_trybot_reclient',
       'linux_optional_gpu_tests_rel': 'gpu_fyi_tests_release_trybot_reclient',
       'linux_upload_clang': 'release_bot',
-      'linux_vr': 'vr_release_trybot',
+      'linux_vr': 'vr_release_trybot_reclient',
       'network_service_linux': 'release_trybot',
     },
 
@@ -1986,10 +1986,6 @@
       'cfi_full', 'cfi_icall', 'cfi_diag', 'cfi_recover', 'thin_lto', 'release', 'static', 'reclient',
     ],
 
-    'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_goma': [
-      'cfi_full', 'cfi_icall', 'cfi_diag', 'thin_lto', 'release', 'static', 'dcheck_always_on', 'goma',
-    ],
-
     'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_reclient': [
       'cfi_full', 'cfi_icall', 'cfi_diag', 'thin_lto', 'release', 'static', 'dcheck_always_on', 'reclient',
     ],
@@ -3680,18 +3676,6 @@
       'ubsan', 'release_bot_reclient',
     ],
 
-    'ubsan_vptr_release_bot': [
-      'ubsan_vptr', 'release_bot',
-    ],
-
-    'ubsan_vptr_release_bot': [
-      'ubsan_vptr', 'ubsan_no_recover_hack', 'release_bot',
-    ],
-
-    'ubsan_vptr_release_bot_reclient': [
-      'ubsan_vptr', 'release_bot_reclient',
-    ],
-
     'ubsan_vptr_release_bot_reclient': [
       'ubsan_vptr', 'ubsan_no_recover_hack', 'release_bot_reclient',
     ],
@@ -3740,8 +3724,8 @@
       'vr', 'release_bot_reclient', 'ozone',
     ],
 
-    'vr_release_trybot': [
-      'vr', 'release_trybot', 'ozone',
+    'vr_release_trybot_reclient': [
+      'vr', 'release_trybot_reclient', 'ozone',
     ],
 
     'win32_arm64_release_bot_reclient': [
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index 39134879..4bc2af4c 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -591,7 +591,7 @@
       "dcheck_always_on": false,
       "is_component_build": false,
       "is_debug": false,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux_chromium_asan_rel_ng": {
@@ -614,7 +614,7 @@
       "use_cfi_cast": true,
       "use_cfi_diag": true,
       "use_cfi_icall": true,
-      "use_goma": true,
+      "use_remoteexec": true,
       "use_thin_lto": true
     }
   },
@@ -684,7 +684,7 @@
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux_chromium_dbg_ng": {
@@ -694,7 +694,7 @@
       "is_debug": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux_chromium_msan_focal": {
@@ -704,7 +704,7 @@
       "is_debug": false,
       "is_msan": true,
       "msan_track_origins": 2,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux_chromium_msan_rel_ng": {
@@ -714,7 +714,7 @@
       "is_debug": false,
       "is_msan": true,
       "msan_track_origins": 2,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux_chromium_tsan_rel_ng": {
@@ -735,7 +735,7 @@
       "is_debug": false,
       "is_ubsan_no_recover": true,
       "is_ubsan_vptr": true,
-      "use_goma": true
+      "use_remoteexec": true
     }
   },
   "linux_layout_tests_layout_ng_disabled": {
@@ -774,8 +774,8 @@
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
-      "use_goma": true,
-      "use_ozone": true
+      "use_ozone": true,
+      "use_remoteexec": true
     }
   },
   "network_service_linux": {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1b73d20..689180e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10851,6 +10851,8 @@
   <int value="288" label="MSDH_INCONSISTENT_VIDEO_TYPE_AND_REQUESTED_FIELDS"/>
   <int value="289"
       label="MSDH_SUPPRESS_LOCAL_AUDIO_PLAYBACK_BUT_AUDIO_NOT_REQUESTED"/>
+  <int value="290" label="MSDH_HOTWORD_ENABLED_BUT_AUDIO_NOT_REQUESTED"/>
+  <int value="291" label="MSDH_DISABLE_LOCAL_ECHO_BUT_AUDIO_NOT_REQUESTED"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">
@@ -26827,6 +26829,12 @@
   <int value="2" label="Bind and video processor blit"/>
 </enum>
 
+<enum name="DirectFromSellerSignalsRequestType">
+  <int value="0" label="Network service fetch"/>
+  <int value="1" label="Cache"/>
+  <int value="2" label="Coalesced"/>
+</enum>
+
 <enum name="DirectoryDatabaseRepairResult">
   <int value="0" label="Succeeded"/>
   <int value="1" label="Failed"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index de6a84e..fa26feb 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -594,6 +594,17 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.BrowserDataMigrator.MoveMigrator.ExtraSpaceRequiredMB"
+    units="MB" expires_after="M121">
+  <owner>ythjkt@chromium.org</owner>
+  <owner>hidehiko@chromium.org</owner>
+  <summary>
+    This is recorded if move profile migration is aborted due to the lack of
+    extra space required on disk. The amount of space required to be freed is
+    recorded.
+  </summary>
+</histogram>
+
 <histogram name="Ash.BrowserDataMigrator.MoveMigrator.PosixErrno.{TaskStatus}"
     units="errno" expires_after="M121">
   <owner>ythjkt@chromium.org</owner>
@@ -4773,7 +4784,7 @@
 </histogram>
 
 <histogram name="Ash.TouchView.TouchViewInactive" units="ms"
-    expires_after="2022-11-03">
+    expires_after="2023-03-19">
   <owner>girard@chromium.org</owner>
   <summary>The length of time between TouchView activations.</summary>
 </histogram>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 03b597c..b070087 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -952,6 +952,26 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.FedCm.SignUp.PrivacyPolicyClicked" enum="BooleanHit"
+    expires_after="M120">
+  <owner>pkotwicz@chromium.org</owner>
+  <owner>web-identity-eng@google.com</owner>
+  <summary>
+    Recorded each time that the privacy-policy link in the FedCM prompt is
+    clicked.
+  </summary>
+</histogram>
+
+<histogram name="Blink.FedCm.SignUp.TermsOfServiceClicked" enum="BooleanHit"
+    expires_after="M120">
+  <owner>pkotwicz@chromium.org</owner>
+  <owner>web-identity-eng@google.com</owner>
+  <summary>
+    Recorded each time that the terms-of-service link in the FedCM prompt is
+    clicked.
+  </summary>
+</histogram>
+
 <histogram name="Blink.FedCm.Status.Csp" enum="FedCmCspStatus"
     expires_after="M120">
   <owner>cbiesinger@chromium.org</owner>
@@ -1925,7 +1945,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.LocalFrameRoot.DidReachFirstContentfulPaint"
+<histogram name="Blink.LocalFrameRoot.DidReachFirstContentfulPaint"
     units="Boolean" expires_after="2023-03-19">
   <owner>pdr@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index de9770d1..ce2ccdee 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -576,6 +576,16 @@
   </summary>
 </histogram>
 
+<histogram name="Compositing.Renderer.CommitHung" units="boolean"
+    expires_after="2023-01-31">
+  <owner>skobes@chromium.org</owner>
+  <owner>input-dev@chromium.org</owner>
+  <summary>
+    Records when the renderer compositor thread is hung (14 seconds) waiting to
+    run the commit after the main thread signalled that it is ready to commit.
+  </summary>
+</histogram>
+
 <histogram
     name="Compositing.Renderer.FirstSurfaceActivationUpdateDuration.{Surface}"
     units="ms" expires_after="2023-04-28">
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index f1d9057..38b2f8e 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -116,6 +116,8 @@
       summary="post-auction forDebuggingOnly.reportAdAuctionLoss() report"/>
   <variant name="DebugWinReport"
       summary="post-auction forDebuggingOnly.reportAdAuctionWin() report"/>
+  <variant name="DirectFromSellerSignals"
+      summary="auction directFromSellerSignals"/>
   <variant name="ScoringScriptJS" summary="seller JavaScript scoring script"/>
   <variant name="SendReportToReport"
       summary="post-auction sendReportTo() report (from either the winning
@@ -428,6 +430,25 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.InterestGroup.Auction.DirectFromSellerSignals.RequestType"
+    enum="DirectFromSellerSignalsRequestType" expires_after="2023-07-31">
+  <owner>caraitto@chromium.org</owner>
+  <owner>pauljensen@chromium.org</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    For each request made for a DirectFromSellerSignals bundle subresource,
+    records whether the request was made to the network service, or whether the
+    request was elided due to either loading from the
+    DirectFromSellerSignalsRequester cache, or coalescing with an exiting
+    request for the same subresource URL.
+
+    Reported at each DirectFromSellerSignalsRequester request.
+
+    See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
+    version of the FLEDGE explainer.
+  </summary>
+</histogram>
+
 <histogram name="Ads.InterestGroup.Auction.First6AuctionsBitsPerPage"
     units="bitfield" expires_after="2023-07-31">
   <owner>caraitto@chromium.org</owner>
@@ -13866,27 +13887,6 @@
   </summary>
 </histogram>
 
-<histogram name="TaskQueueManager.ActiveQueuesCount" units="units"
-    expires_after="M85">
-  <owner>altimin@chromium.org</owner>
-  <owner>farahcharab@chromium.org</owner>
-  <summary>
-    Used to track the number of active task queues in the task queue manager.
-    Reported every time a task is selected for execution.
-  </summary>
-</histogram>
-
-<histogram name="TaskQueueSelector.TaskServicedPerSelectorLogic"
-    enum="TaskQueueSelectorLogic" expires_after="M85">
-  <owner>altimin@chromium.org</owner>
-  <owner>farahcharab@chromium.org</owner>
-  <summary>
-    Used to track the number of tasks serviced due to starvation versus the
-    number of tasks serviced due to priroity. Incremented whenever a task queue
-    is selected to service.
-  </summary>
-</histogram>
-
 <histogram name="ThirdPartyModules.Certificates.Microsoft" units="certificates"
     expires_after="M85">
   <owner>chrisha@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 5761d9e..b34c0387 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -833,16 +833,6 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.NoCachedPopulationReason"
-    enum="SafeBrowsingNoCachedPopulationReason" expires_after="2022-11-11">
-  <owner>drubery@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Whenever Safe Browsing sends a request with no cached user population to
-    compare it to, we record the reason the cache is empty.
-  </summary>
-</histogram>
-
 <histogram name="SafeBrowsing.PageLoadToken.ClearReason"
     enum="SafeBrowsingPageLoadTokenClearReason" expires_after="2023-03-05">
   <owner>xinghuilu@chromium.org</owner>
@@ -922,23 +912,6 @@
   </token>
 </histogram>
 
-<histogram name="SafeBrowsing.PopulationMatchesCachedValue.{Field}"
-    enum="BooleanMatched" expires_after="2022-11-11">
-  <owner>drubery@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Whenever the ChromeUserPopulation proto is created (almost every time Safe
-    Browsing is contacted), we compare it to a cached value. This histogram
-    records whether the cached value has the same value in {Field} as the newly
-    created value.
-  </summary>
-  <token key="Field">
-    <variant name="Mbb" summary="Make Browsing Better preference"/>
-    <variant name="Population" summary="Safe Browsing population"/>
-    <variant name="UserAgent" summary="User agent"/>
-  </token>
-</histogram>
-
 <histogram name="SafeBrowsing.Pref.Daily.Extended" enum="BooleanEnabled"
     expires_after="2023-04-30">
   <owner>xinghuilu@chromium.org</owner>
@@ -1572,7 +1545,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.TailoredSecurityUnconsentedMessageDismissReason"
-    enum="MessageDismissReason" expires_after="2022-11-11">
+    enum="MessageDismissReason" expires_after="2023-11-11">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1583,7 +1556,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.TailoredSecurityUnconsentedModalOutcome"
-    enum="SafeBrowsingTailoredSecurityOutcome" expires_after="2022-11-11">
+    enum="SafeBrowsingTailoredSecurityOutcome" expires_after="2023-11-11">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1606,7 +1579,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.TailoredSecurityUnconsented{Flow}MessageOutcome"
-    enum="SafeBrowsingTailoredSecurityOutcome" expires_after="2022-11-11">
+    enum="SafeBrowsingTailoredSecurityOutcome" expires_after="2023-11-11">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b1d4143..1f3903d9d 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v30.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "3fe8d9f8334d7872cebb94cd83f6f1d4b0965670",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/cb8281edbb0e244234ce27dbf69b71c1334bc836/trace_processor_shell"
+            "hash": "2ef6d0c4d75184e5139cfab6a4485ddfd488a1ae",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/0cbb37ad93ee2bfcf4c1b0bc3e136eadbe4fb467/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 3a325267..074e35a 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -1047,15 +1047,22 @@
         // '\n' character in a <br> element unless multiple consecutive <br>
         // elements are present and so empty paragraphs have been created.
         //
-        // Note that `!AtStartOfAnchor()` implies that `MaxTextOffset()` > 0 and
-        // `text_offset()` > 0. Therefore,
-        // `text_position->GetText().at(text_position->text_offset_ - 1)` will
-        // always be valid.
+        // Note that, in theory, `!AtStartOfAnchor()` implies that
+        // `MaxTextOffset()` > 0 and `text_offset()` > 0. Therefore,
+        // `text_position->GetText().at(text_position->text_offset_ - 1)` should
+        // always be valid. However, as reported by https://crbug.com/1379716,
+        // this logic appears to have flaws.
+        //
+        // TODO(accessibility): Investigate what are these edge cases that lead
+        // to have a `text_offset_` greater than or equal to the `text` length.
         if (!text_position->AtStartOfAnchor()) {
-          if (!text_position->IsPointingToLineBreak() &&
-              text_position->GetText().at(text_position->text_offset_ - 1) ==
-                  '\n') {
-            return true;
+          if (!text_position->IsPointingToLineBreak()) {
+            const std::u16string text = text_position->GetText();
+            if (static_cast<size_t>(text_position->text_offset_) <
+                    text.length() &&
+                text.at(text_position->text_offset_) == '\n') {
+              return true;
+            }
           }
           return false;
         }
@@ -1116,16 +1123,23 @@
         // character in a <br> element unless multiple consecutive <br> elements
         // are present and so empty paragraphs have been created.
         //
-        // Note that `!AtEndOfAnchor()` implies `AtStartOfAnchor()` !=
-        // `AtEndOfAnchor()` which in turn implies that `MaxTextOffset()` > 0
-        // and `text_offset()` < `MaxTextOffset()`. Therefore,
-        // `text_position->GetText().at(text_position->text_offset_)` will
-        // always be valid.
+        // Note that, in theory, `!AtEndOfAnchor()` implies
+        // `AtStartOfAnchor()` != `AtEndOfAnchor()` which in turn implies that
+        // `MaxTextOffset()` > 0 and `text_offset()` < `MaxTextOffset()`.
+        // Therefore, `text_position->GetText().at(text_position->text_offset_)`
+        // should always be valid. However, as reported by
+        // https://crbug.com/1379716, this logic appears to have flaws.
+        //
+        // TODO(accessibility): Investigate what are these edge cases that lead
+        // to have a `text_offset_` greater than or equal to the `text` length.
         if (!text_position->AtEndOfAnchor()) {
-          if (!text_position->IsPointingToLineBreak() &&
-              text_position->GetText().at(text_position->text_offset_) ==
-                  '\n') {
-            return true;
+          if (!text_position->IsPointingToLineBreak()) {
+            const std::u16string text = text_position->GetText();
+            if (static_cast<size_t>(text_position->text_offset_) <
+                    text.length() &&
+                text.at(text_position->text_offset_) == '\n') {
+              return true;
+            }
           }
           return false;
         }
diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn
index 8eb556b9f..11de200 100644
--- a/ui/accessibility/platform/BUILD.gn
+++ b/ui/accessibility/platform/BUILD.gn
@@ -248,6 +248,8 @@
         "ax_platform_node_auralinux.h",
         "inspect/ax_call_statement_invoker_auralinux.cc",
         "inspect/ax_call_statement_invoker_auralinux.h",
+        "inspect/ax_event_recorder_auralinux.cc",
+        "inspect/ax_event_recorder_auralinux.h",
         "inspect/ax_inspect_utils_auralinux.cc",
         "inspect/ax_inspect_utils_auralinux.h",
         "inspect/ax_tree_formatter_auralinux.cc",
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/ui/accessibility/platform/inspect/ax_event_recorder_auralinux.cc
similarity index 88%
rename from content/browser/accessibility/accessibility_event_recorder_auralinux.cc
rename to ui/accessibility/platform/inspect/ax_event_recorder_auralinux.cc
index 8cbf52bf..3f6f678a 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
+++ b/ui/accessibility/platform/inspect/ax_event_recorder_auralinux.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 "content/browser/accessibility/accessibility_event_recorder_auralinux.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder_auralinux.h"
 
 #include <atk/atk.h>
 #include <atk/atkutil.h>
@@ -10,22 +10,15 @@
 
 #include "base/no_destructor.h"
 #include "base/process/process_handle.h"
-#include "base/ranges/algorithm.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "content/browser/accessibility/browser_accessibility_auralinux.h"
 #include "ui/accessibility/platform/ax_platform_tree_manager.h"
 #include "ui/accessibility/platform/inspect/ax_inspect_utils_auralinux.h"
 
-namespace content {
-
-using ui::AtkRoleToString;
-using ui::ATSPIRoleToString;
-using ui::ATSPIStateToString;
-using ui::FindAccessible;
+namespace ui {
 
 // static
-AccessibilityEventRecorderAuraLinux*
-    AccessibilityEventRecorderAuraLinux::instance_ = nullptr;
+AXEventRecorderAuraLinux* AXEventRecorderAuraLinux::instance_ = nullptr;
 
 // static
 std::vector<unsigned int>& GetATKListenerIds() {
@@ -34,7 +27,7 @@
 }
 
 // static
-gboolean AccessibilityEventRecorderAuraLinux::OnATKEventReceived(
+gboolean AXEventRecorderAuraLinux::OnATKEventReceived(
     GSignalInvocationHint* hint,
     unsigned int n_params,
     const GValue* params,
@@ -55,14 +48,14 @@
   return true;
 }
 
-bool AccessibilityEventRecorderAuraLinux::ShouldUseATSPI() {
+bool AXEventRecorderAuraLinux::ShouldUseATSPI() {
   return pid_ != base::GetCurrentProcId() || !selector_.empty();
 }
 
-AccessibilityEventRecorderAuraLinux::AccessibilityEventRecorderAuraLinux(
-    ui::AXPlatformTreeManager* manager,
+AXEventRecorderAuraLinux::AXEventRecorderAuraLinux(
+    AXPlatformTreeManager* manager,
     base::ProcessId pid,
-    const ui::AXTreeSelector& selector)
+    const AXTreeSelector& selector)
     : manager_(manager), pid_(pid), selector_(selector) {
   CHECK(!instance_) << "There can be only one instance of"
                     << " AccessibilityEventRecorder at a time.";
@@ -76,13 +69,12 @@
   instance_ = this;
 }
 
-AccessibilityEventRecorderAuraLinux::~AccessibilityEventRecorderAuraLinux() {
+AXEventRecorderAuraLinux::~AXEventRecorderAuraLinux() {
   RemoveATSPIEventListeners();
   instance_ = nullptr;
 }
 
-void AccessibilityEventRecorderAuraLinux::AddATKEventListener(
-    const char* event_name) {
+void AXEventRecorderAuraLinux::AddATKEventListener(const char* event_name) {
   unsigned id = atk_add_global_event_listener(OnATKEventReceived, event_name);
   if (!id)
     LOG(FATAL) << "atk_add_global_event_listener failed for " << event_name;
@@ -91,7 +83,7 @@
   atk_listener_ids.push_back(id);
 }
 
-void AccessibilityEventRecorderAuraLinux::AddATKEventListeners() {
+void AXEventRecorderAuraLinux::AddATKEventListeners() {
   if (GetATKListenerIds().size() >= 1)
     return;
   GObject* gobject = G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr, nullptr));
@@ -113,7 +105,7 @@
   AddATKEventListener("ATK:AtkTable:row-reordered");
 }
 
-void AccessibilityEventRecorderAuraLinux::RemoveATKEventListeners() {
+void AXEventRecorderAuraLinux::RemoveATKEventListeners() {
   std::vector<unsigned int>& atk_listener_ids = GetATKListenerIds();
   for (const auto& id : atk_listener_ids)
     atk_remove_global_event_listener(id);
@@ -121,9 +113,8 @@
   atk_listener_ids.clear();
 }
 
-std::string AccessibilityEventRecorderAuraLinux::AtkObjectToString(
-    AtkObject* obj,
-    bool include_name) {
+std::string AXEventRecorderAuraLinux::AtkObjectToString(AtkObject* obj,
+                                                        bool include_name) {
   std::string role = AtkRoleToString(atk_object_get_role(obj));
   base::ReplaceChars(role, " ", "_", &role);
   std::string str =
@@ -135,10 +126,9 @@
   return str;
 }
 
-void AccessibilityEventRecorderAuraLinux::ProcessATKEvent(
-    const char* event,
-    unsigned int n_params,
-    const GValue* params) {
+void AXEventRecorderAuraLinux::ProcessATKEvent(const char* event,
+                                               unsigned int n_params,
+                                               const GValue* params) {
   // If we don't have a root object, it means the tree is being destroyed.
   if (!manager_->RootDelegate()) {
     RemoveATKEventListeners();
@@ -283,12 +273,11 @@
 };
 
 static void OnATSPIEventReceived(AtspiEvent* event, void* data) {
-  static_cast<AccessibilityEventRecorderAuraLinux*>(data)->ProcessATSPIEvent(
-      event);
+  static_cast<AXEventRecorderAuraLinux*>(data)->ProcessATSPIEvent(event);
   g_boxed_free(ATSPI_TYPE_EVENT, static_cast<void*>(event));
 }
 
-void AccessibilityEventRecorderAuraLinux::AddATSPIEventListeners() {
+void AXEventRecorderAuraLinux::AddATSPIEventListeners() {
   atspi_init();
   atspi_event_listener_ =
       atspi_event_listener_new(OnATSPIEventReceived, this, nullptr);
@@ -304,7 +293,7 @@
   }
 }
 
-void AccessibilityEventRecorderAuraLinux::RemoveATSPIEventListeners() {
+void AXEventRecorderAuraLinux::RemoveATSPIEventListeners() {
   if (!atspi_event_listener_)
     return;
 
@@ -323,8 +312,7 @@
   atspi_event_listener_ = nullptr;
 }
 
-void AccessibilityEventRecorderAuraLinux::ProcessATSPIEvent(
-    const AtspiEvent* event) {
+void AXEventRecorderAuraLinux::ProcessATSPIEvent(const AtspiEvent* event) {
   GError* error = nullptr;
 
   // Ignore irrelevant events, i.e. fired for other applications.
@@ -399,4 +387,4 @@
   OnEvent(output.str());
 }
 
-}  // namespace content
+}  // namespace ui
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.h b/ui/accessibility/platform/inspect/ax_event_recorder_auralinux.h
similarity index 66%
rename from content/browser/accessibility/accessibility_event_recorder_auralinux.h
rename to ui/accessibility/platform/inspect/ax_event_recorder_auralinux.h
index 43cf43d3..2e76415 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.h
+++ b/ui/accessibility/platform/inspect/ax_event_recorder_auralinux.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_AURALINUX_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_AURALINUX_H_
+#ifndef UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_AURALINUX_H_
+#define UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_AURALINUX_H_
 
 #include <atk/atk.h>
 #include <atspi/atspi.h>
 
 #include "base/memory/raw_ptr.h"
 #include "base/process/process_handle.h"
-#include "content/common/content_export.h"
+#include "ui/accessibility/ax_export.h"
 #include "ui/accessibility/platform/inspect/ax_event_recorder.h"
 #include "ui/accessibility/platform/inspect/ax_inspect.h"
 
@@ -18,10 +18,6 @@
 
 class AXPlatformTreeManager;
 
-}  // namespace ui
-
-namespace content {
-
 // This class has two distinct event recording code paths. When we are
 // recording events in-process (typically this is used for
 // DumpAccessibilityEvents tests), we use ATK's global event handlers. Since
@@ -31,20 +27,16 @@
 // TODO(crbug.com/1133330) AT-SPI2 should be capable of intercepting events
 // in-process as well, thus it should be possible to remove the ATK code path
 // entirely.
-class CONTENT_EXPORT AccessibilityEventRecorderAuraLinux
-    : public ui::AXEventRecorder {
+class AX_EXPORT AXEventRecorderAuraLinux : public AXEventRecorder {
  public:
-  explicit AccessibilityEventRecorderAuraLinux(
-      ui::AXPlatformTreeManager* manager,
-      base::ProcessId pid,
-      const ui::AXTreeSelector& selector);
+  AXEventRecorderAuraLinux(AXPlatformTreeManager* manager,
+                           base::ProcessId pid,
+                           const AXTreeSelector& selector);
 
-  AccessibilityEventRecorderAuraLinux(
-      const AccessibilityEventRecorderAuraLinux&) = delete;
-  AccessibilityEventRecorderAuraLinux& operator=(
-      const AccessibilityEventRecorderAuraLinux&) = delete;
+  AXEventRecorderAuraLinux(const AXEventRecorderAuraLinux&) = delete;
+  AXEventRecorderAuraLinux& operator=(const AXEventRecorderAuraLinux&) = delete;
 
-  ~AccessibilityEventRecorderAuraLinux() override;
+  ~AXEventRecorderAuraLinux() override;
 
   void ProcessATKEvent(const char* event,
                        unsigned int n_params,
@@ -70,12 +62,12 @@
 
   raw_ptr<AtspiEventListener> atspi_event_listener_ = nullptr;
   // TODO: should be either removed or converted to a weakptr.
-  const raw_ptr<ui::AXPlatformTreeManager> manager_;
+  const raw_ptr<AXPlatformTreeManager> manager_;
   base::ProcessId pid_;
-  ui::AXTreeSelector selector_;
-  static AccessibilityEventRecorderAuraLinux* instance_;
+  AXTreeSelector selector_;
+  static AXEventRecorderAuraLinux* instance_;
 };
 
-}  // namespace content
+}  // namespace ui
 
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_AURALINUX_H_
+#endif  // UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_AURALINUX_H_
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 813deda1..8603662 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -185,6 +185,9 @@
   E_CPONLY(kColorIcon) \
   E_CPONLY(kColorIconDisabled) \
   E_CPONLY(kColorIconSecondary) \
+  /* This is declared here so //components can access it, but we expect \
+   * this to be set in the embedder. */ \
+  E_CPONLY(kColorInfoBarIcon) \
   E_CPONLY(kColorLabelForeground) \
   E_CPONLY(kColorLabelForegroundDisabled) \
   E_CPONLY(kColorLabelForegroundSecondary) \
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc
index 3e7ef72..ffe9479 100644
--- a/ui/color/ui_color_mixer.cc
+++ b/ui/color/ui_color_mixer.cc
@@ -69,6 +69,7 @@
   mixer[kColorIcon] = {kColorSecondaryForeground};
   mixer[kColorIconDisabled] = SetAlpha(kColorIcon, gfx::kDisabledControlAlpha);
   mixer[kColorIconSecondary] = {gfx::kGoogleGrey600};
+  mixer[kColorInfoBarIcon] = {kColorAccent};
   mixer[kColorLabelForeground] = {kColorPrimaryForeground};
   mixer[kColorLabelForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorLabelForegroundSecondary] = {kColorSecondaryForeground};
diff --git a/ui/gfx/geometry/test/geometry_util.cc b/ui/gfx/geometry/test/geometry_util.cc
index bd304a4..502f2df 100644
--- a/ui/gfx/geometry/test/geometry_util.cc
+++ b/ui/gfx/geometry/test/geometry_util.cc
@@ -211,13 +211,6 @@
   return ::testing::AssertionSuccess();
 }
 
-Transform InvertAndCheck(const Transform& transform) {
-  Transform result(Transform::kSkipInitialization);
-  bool inverted_successfully = transform.GetInverse(&result);
-  DCHECK(inverted_successfully);
-  return result;
-}
-
 ::testing::AssertionResult AssertBoxFloatEqual(const char* lhs_expr,
                                                const char* rhs_expr,
                                                const BoxF& lhs,
diff --git a/ui/gfx/geometry/test/geometry_util.h b/ui/gfx/geometry/test/geometry_util.h
index 4706f11..57968bae8 100644
--- a/ui/gfx/geometry/test/geometry_util.h
+++ b/ui/gfx/geometry/test/geometry_util.h
@@ -101,10 +101,6 @@
     const DecomposedTransform& rhs,
     float abs_error);
 
-// Should be used in test code only, for convenience. Production code should use
-// the gfx::Transform::GetInverse() API.
-Transform InvertAndCheck(const Transform& transform);
-
 #define EXPECT_BOXF_EQ(a, b) \
   EXPECT_PRED_FORMAT2(::gfx::AssertBoxFloatEqual, a, b)
 
diff --git a/ui/gfx/geometry/transform.cc b/ui/gfx/geometry/transform.cc
index e3c0080e..d8e8f37 100644
--- a/ui/gfx/geometry/transform.cc
+++ b/ui/gfx/geometry/transform.cc
@@ -6,6 +6,7 @@
 
 #include "base/check_op.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/angle_conversions.h"
 #include "ui/gfx/geometry/axis_transform2d.h"
@@ -75,7 +76,6 @@
 
 Transform::Transform() = default;
 Transform::~Transform() = default;
-Transform::Transform(SkipInitialization) {}
 Transform::Transform(Transform&&) = default;
 Transform& Transform::operator=(Transform&&) = default;
 
@@ -490,9 +490,9 @@
     return false;
   }
 
-  if (transform != this) {
+  if (!transform->matrix_)
     transform->matrix_ = std::make_unique<Matrix44>(Matrix44::kUninitialized);
-  }
+
   if (matrix_->GetInverse(*transform->matrix_))
     return true;
 
@@ -502,6 +502,20 @@
   return false;
 }
 
+Transform Transform::GetCheckedInverse() const {
+  Transform inverse;
+  if (!GetInverse(&inverse))
+    NOTREACHED() << ToString() << " is not invertible";
+  return inverse;
+}
+
+Transform Transform::InverseOrIdentity() const {
+  Transform inverse;
+  bool invertible = GetInverse(&inverse);
+  DCHECK(invertible || inverse.IsIdentity());
+  return inverse;
+}
+
 bool Transform::Preserves2dAxisAlignment() const {
   if (LIKELY(!matrix_))
     return true;
@@ -732,7 +746,7 @@
       return axis_2d_.InverseMapRect(rect);
   }
 
-  Transform inverse(kSkipInitialization);
+  Transform inverse;
   if (!GetInverse(&inverse))
     return absl::nullopt;
 
diff --git a/ui/gfx/geometry/transform.h b/ui/gfx/geometry/transform.h
index 6861d12..7ffb8f8 100644
--- a/ui/gfx/geometry/transform.h
+++ b/ui/gfx/geometry/transform.h
@@ -47,10 +47,6 @@
   Transform();
   ~Transform();
 
-  // TODO(crbug.com/1359528): This is same as Transform(). Remove this.
-  enum SkipInitialization { kSkipInitialization };
-  explicit Transform(SkipInitialization);
-
   Transform(const Transform& rhs);
   Transform& operator=(const Transform& rhs);
   Transform(Transform&&);
@@ -341,14 +337,24 @@
     return LIKELY(!matrix_) ? axis_2d_.IsInvertible() : matrix_->IsInvertible();
   }
 
+  // If |this| is invertible, inverts |this| and stores the result in
+  // |*transform|, and returns true. Otherwise sets |*transform| to identity
+  // and returns false.
+  [[nodiscard]] bool GetInverse(Transform* transform) const;
+
+  // Same as above except that it assumes success, otherwise DCHECK fails.
+  // This is suitable when the transform is known to be invertible.
+  [[nodiscard]] Transform GetCheckedInverse() const;
+
+  // Same as GetInverse() except that it returns the the inverse of |this| or
+  // identity, instead of a bool. This is suitable when it's good to fallback
+  // to identity silently.
+  [[nodiscard]] Transform InverseOrIdentity() const;
+
   // Returns true if a layer with a forward-facing normal of (0, 0, 1) would
   // have its back side facing frontwards after applying the transform.
   bool IsBackFaceVisible() const;
 
-  // Inverts the transform which is passed in. Returns true if successful, or
-  // sets |transform| to the identify matrix on failure.
-  [[nodiscard]] bool GetInverse(Transform* transform) const;
-
   // Transposes this transform in place.
   void Transpose();
 
diff --git a/ui/gfx/geometry/transform_unittest.cc b/ui/gfx/geometry/transform_unittest.cc
index d23edb7..8de9ac5 100644
--- a/ui/gfx/geometry/transform_unittest.cc
+++ b/ui/gfx/geometry/transform_unittest.cc
@@ -1354,14 +1354,22 @@
   EXPECT_FALSE(transform.IsIdentityOrIntegerTranslation());
 }
 
-TEST(XFormTest, verifyMatrixInversion) {
+TEST(XFormTest, Inverse) {
+  {
+    Transform identity;
+    Transform inverse_identity;
+    EXPECT_TRUE(identity.GetInverse(&inverse_identity));
+    EXPECT_EQ(identity, inverse_identity);
+    EXPECT_EQ(identity, identity.InverseOrIdentity());
+  }
+
   {
     // Invert a translation
-    gfx::Transform translation;
+    Transform translation;
     translation.Translate3d(2.0, 3.0, 4.0);
     EXPECT_TRUE(translation.IsInvertible());
 
-    gfx::Transform inverse_translation;
+    Transform inverse_translation;
     bool is_invertible = translation.GetInverse(&inverse_translation);
     EXPECT_TRUE(is_invertible);
     EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, -2.0f, inverse_translation);
@@ -1369,6 +1377,8 @@
     EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, -4.0f, inverse_translation);
     EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_translation);
 
+    EXPECT_EQ(inverse_translation, translation.InverseOrIdentity());
+
     // GetInverse with the parameter pointing to itself.
     EXPECT_TRUE(translation.GetInverse(&translation));
     EXPECT_EQ(translation, inverse_translation);
@@ -1376,30 +1386,50 @@
 
   {
     // Invert a non-uniform scale
-    gfx::Transform scale;
+    Transform scale;
     scale.Scale3d(4.0, 10.0, 100.0);
     EXPECT_TRUE(scale.IsInvertible());
 
-    gfx::Transform inverse_scale;
+    Transform inverse_scale;
     bool is_invertible = scale.GetInverse(&inverse_scale);
     EXPECT_TRUE(is_invertible);
     EXPECT_ROW1_EQ(0.25f, 0.0f, 0.0f, 0.0f, inverse_scale);
     EXPECT_ROW2_EQ(0.0f, 0.1f, 0.0f, 0.0f, inverse_scale);
     EXPECT_ROW3_EQ(0.0f, 0.0f, 0.01f, 0.0f, inverse_scale);
     EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_scale);
+
+    EXPECT_EQ(inverse_scale, scale.InverseOrIdentity());
+  }
+
+  {
+    Transform m1;
+    m1.RotateAboutZAxis(-30);
+    m1.RotateAboutYAxis(10);
+    m1.RotateAboutXAxis(20);
+    m1.ApplyPerspectiveDepth(100);
+    Transform m2;
+    m2.ApplyPerspectiveDepth(-100);
+    m2.RotateAboutXAxis(-20);
+    m2.RotateAboutYAxis(-10);
+    m2.RotateAboutZAxis(30);
+    Transform inverse_m1, inverse_m2;
+    EXPECT_TRUE(m1.GetInverse(&inverse_m1));
+    EXPECT_TRUE(m2.GetInverse(&inverse_m2));
+    EXPECT_TRANSFORM_NEAR(m1, inverse_m2, 1e-6);
+    EXPECT_TRANSFORM_NEAR(m2, inverse_m1, 1e-6);
   }
 
   {
     // Try to invert a matrix that is not invertible.
     // The inverse() function should reset the output matrix to identity.
-    gfx::Transform uninvertible;
+    Transform uninvertible;
     uninvertible.set_rc(0, 0, 0.f);
     uninvertible.set_rc(1, 1, 0.f);
     uninvertible.set_rc(2, 2, 0.f);
     uninvertible.set_rc(3, 3, 0.f);
     EXPECT_FALSE(uninvertible.IsInvertible());
 
-    gfx::Transform inverse_of_uninvertible;
+    Transform inverse_of_uninvertible;
 
     // Add a scale just to more easily ensure that inverse_of_uninvertible is
     // reset to identity.
@@ -1412,6 +1442,8 @@
     EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, inverse_of_uninvertible);
     EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, inverse_of_uninvertible);
     EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_of_uninvertible);
+
+    EXPECT_EQ(inverse_of_uninvertible, uninvertible.InverseOrIdentity());
   }
 }
 
diff --git a/ui/gl/gl_image_native_pixmap.cc b/ui/gl/gl_image_native_pixmap.cc
index 85a4d85..d492ad8f 100644
--- a/ui/gl/gl_image_native_pixmap.cc
+++ b/ui/gl/gl_image_native_pixmap.cc
@@ -245,13 +245,6 @@
   return true;
 }
 
-bool GLImageNativePixmap::InitializeForOverlay(
-    scoped_refptr<gfx::NativePixmap> pixmap) {
-  DCHECK(!pixmap_);
-  pixmap_ = pixmap;
-  return true;
-}
-
 bool GLImageNativePixmap::InitializeFromTexture(uint32_t texture_id) {
   if (GLInternalFormat(format_) == GL_NONE) {
     LOG(ERROR) << "Unsupported format: " << gfx::BufferFormatToString(format_);
diff --git a/ui/gl/gl_image_native_pixmap.h b/ui/gl/gl_image_native_pixmap.h
index c4f9b3cd..59bd313 100644
--- a/ui/gl/gl_image_native_pixmap.h
+++ b/ui/gl/gl_image_native_pixmap.h
@@ -23,7 +23,6 @@
 
   // Create an EGLImage from a given NativePixmap.
   bool Initialize(scoped_refptr<gfx::NativePixmap> pixmap);
-  bool InitializeForOverlay(scoped_refptr<gfx::NativePixmap> pixmap);
   // Create an EGLImage from a given GL texture.
   bool InitializeFromTexture(uint32_t texture_id);
   // Export the wrapped EGLImage to dmabuf fds.
diff --git a/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.cc b/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.cc
index 9a66eb3..e483bdf 100644
--- a/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.cc
+++ b/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.cc
@@ -621,7 +621,7 @@
       std::make_unique<base::MessagePumpForIO::ZxHandleWatchController>(
           FROM_HERE);
   bool watch_result = base::CurrentIOThread::Get()->WatchZxHandle(
-      handle_.get(), /*persistent=*/true, ZX_EVENTPAIR_PEER_CLOSED,
+      handle_.get(), /*persistent=*/false, ZX_EVENTPAIR_PEER_CLOSED,
       handle_watch_.get(), this);
 
   if (!watch_result) {
@@ -674,5 +674,7 @@
   for (auto& callback : on_released_) {
     std::move(callback).Run();
   }
+  on_released_.clear();
 }
+
 }  // namespace ui
diff --git a/ui/ozone/platform/scenic/scenic_window.cc b/ui/ozone/platform/scenic/scenic_window.cc
index 5d225bd..4bc521e 100644
--- a/ui/ozone/platform/scenic/scenic_window.cc
+++ b/ui/ozone/platform/scenic/scenic_window.cc
@@ -244,7 +244,6 @@
 }
 
 PlatformWindowState ScenicWindow::GetPlatformWindowState() const {
-  NOTIMPLEMENTED_LOG_ONCE();
   if (is_fullscreen_)
     return PlatformWindowState::kFullScreen;
   if (!is_view_attached_)
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
index 55f9b93..64ef074 100644
--- a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
+++ b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
@@ -579,7 +579,7 @@
       std::make_unique<base::MessagePumpForIO::ZxHandleWatchController>(
           FROM_HERE);
   bool watch_result = base::CurrentIOThread::Get()->WatchZxHandle(
-      handle_.get(), true /* persistent */, ZX_EVENTPAIR_PEER_CLOSED,
+      handle_.get(), /*persistent=*/false, ZX_EVENTPAIR_PEER_CLOSED,
       handle_watch_.get(), this);
 
   if (!watch_result) {
@@ -632,6 +632,7 @@
   for (auto& callback : on_released_) {
     std::move(callback).Run();
   }
+  on_released_.clear();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_output.cc b/ui/ozone/platform/wayland/host/wayland_output.cc
index a5a6ee7..bb133cca 100644
--- a/ui/ozone/platform/wayland/host/wayland_output.cc
+++ b/ui/ozone/platform/wayland/host/wayland_output.cc
@@ -61,6 +61,27 @@
   connection->wayland_output_manager_->AddWaylandOutput(name, output.release());
 }
 
+WaylandOutput::Metrics::Metrics() = default;
+WaylandOutput::Metrics::Metrics(Id output_id,
+                                gfx::Point origin,
+                                gfx::Size logical_size,
+                                gfx::Size physical_size,
+                                gfx::Insets insets,
+                                float scale_factor,
+                                int32_t panel_transform,
+                                int32_t logical_transform,
+                                const std::string& description)
+    : output_id(output_id),
+      origin(origin),
+      logical_size(logical_size),
+      physical_size(physical_size),
+      insets(insets),
+      scale_factor(scale_factor),
+      panel_transform(panel_transform),
+      logical_transform(logical_transform),
+      description(description) {}
+WaylandOutput::Metrics::~Metrics() = default;
+
 WaylandOutput::WaylandOutput(Id output_id,
                              wl_output* output,
                              WaylandConnection* connection)
@@ -116,6 +137,13 @@
              : scale_factor();
 }
 
+WaylandOutput::Metrics WaylandOutput::GetMetrics() const {
+  // TODO(aluh): Change to designated initializers once C++20 is supported.
+  return {output_id(),  origin(),       logical_size(),    physical_size(),
+          insets(),     scale_factor(), panel_transform(), logical_transform(),
+          description()};
+}
+
 int32_t WaylandOutput::logical_transform() const {
   if (aura_output_ && aura_output_->logical_transform()) {
     return *aura_output_->logical_transform();
@@ -168,9 +196,7 @@
       scale_factor_ = max_physical_side / max_logical_side;
     }
   }
-  delegate_->OnOutputHandleMetrics(
-      output_id_, origin(), logical_size(), physical_size_, insets(),
-      scale_factor_, panel_transform_, logical_transform(), description());
+  delegate_->OnOutputHandleMetrics(GetMetrics());
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/wayland_output.h b/ui/ozone/platform/wayland/host/wayland_output.h
index 31f7c50..8c2463e 100644
--- a/ui/ozone/platform/wayland/host/wayland_output.h
+++ b/ui/ozone/platform/wayland/host/wayland_output.h
@@ -42,17 +42,39 @@
                           const std::string& interface,
                           uint32_t version);
 
+  // All parameters are in DIP screen coordinates/units except |physical_size|,
+  // which is in physical pixels.
+  struct Metrics {
+    // TODO(aluh): Remove explicit constructors/destructor to enable aggregate
+    // initialization if chromium-style check for complex struct is removed.
+    // See:
+    // https://groups.google.com/a/google.com/g/chromeos-chatty-eng/c/nM1_QC6qcuA
+    Metrics();
+    Metrics(Id output_id,
+            gfx::Point origin,
+            gfx::Size logical_size,
+            gfx::Size physical_size,
+            gfx::Insets insets,
+            float scale_factor,
+            int32_t panel_transform,
+            int32_t logical_transform,
+            const std::string& description);
+    ~Metrics();
+
+    Id output_id = 0;
+    gfx::Point origin;
+    gfx::Size logical_size;
+    gfx::Size physical_size;
+    gfx::Insets insets;
+    float scale_factor = 0.0;
+    int32_t panel_transform = 0;
+    int32_t logical_transform = 0;
+    std::string description;
+  };
+
   class Delegate {
    public:
-    virtual void OnOutputHandleMetrics(Id output_id,
-                                       const gfx::Point& origin,
-                                       const gfx::Size& logical_size,
-                                       const gfx::Size& physical_size,
-                                       const gfx::Insets& insets,
-                                       float scale_factor,
-                                       int32_t panel_transform,
-                                       int32_t logical_transform,
-                                       const std::string& description) = 0;
+    virtual void OnOutputHandleMetrics(const Metrics& metrics) = 0;
 
    protected:
     virtual ~Delegate() = default;
@@ -71,6 +93,7 @@
   void InitializeColorManagementOutput(WaylandZcrColorManager* manager);
   float GetUIScaleFactor() const;
 
+  Metrics GetMetrics() const;
   Id output_id() const { return output_id_; }
   bool has_output(wl_output* output) const { return output_.get() == output; }
   float scale_factor() const { return scale_factor_; }
diff --git a/ui/ozone/platform/wayland/host/wayland_output_manager.cc b/ui/ozone/platform/wayland/host/wayland_output_manager.cc
index ad29fbf0..a5d96045 100644
--- a/ui/ozone/platform/wayland/host/wayland_output_manager.cc
+++ b/ui/ozone/platform/wayland/host/wayland_output_manager.cc
@@ -117,12 +117,7 @@
   // changes.
   for (const auto& output : output_list_) {
     if (output.second->is_ready()) {
-      screen->OnOutputAddedOrUpdated(
-          output.second->output_id(), output.second->origin(),
-          output.second->logical_size(), output.second->physical_size(),
-          output.second->insets(), output.second->scale_factor(),
-          output.second->panel_transform(), output.second->logical_transform(),
-          output.second->description());
+      screen->OnOutputAddedOrUpdated(output.second->GetMetrics());
     }
   }
 }
@@ -147,19 +142,9 @@
 }
 
 void WaylandOutputManager::OnOutputHandleMetrics(
-    WaylandOutput::Id output_id,
-    const gfx::Point& origin,
-    const gfx::Size& logical_size,
-    const gfx::Size& physical_size,
-    const gfx::Insets& insets,
-    float scale_factor,
-    int32_t panel_transform,
-    int32_t logical_transform,
-    const std::string& description) {
+    const WaylandOutput::Metrics& metrics) {
   if (wayland_screen_) {
-    wayland_screen_->OnOutputAddedOrUpdated(
-        output_id, origin, logical_size, physical_size, insets, scale_factor,
-        panel_transform, logical_transform, description);
+    wayland_screen_->OnOutputAddedOrUpdated(metrics);
   }
 
   // Update scale of the windows currently associated with |output_id|. i.e:
@@ -167,10 +152,11 @@
   // which have not yet entered any output (i.e: no wl_surface.enter event
   // received for their root surface) and |output_id| is the primary output.
   const bool is_primary =
-      wayland_screen_ && output_id == wayland_screen_->GetPrimaryDisplay().id();
+      wayland_screen_ &&
+      metrics.output_id == wayland_screen_->GetPrimaryDisplay().id();
   for (auto* window : connection_->wayland_window_manager()->GetAllWindows()) {
     auto entered_output = window->GetPreferredEnteredOutputId();
-    if (entered_output == output_id || (!entered_output && is_primary))
+    if (entered_output == metrics.output_id || (!entered_output && is_primary))
       window->UpdateWindowScale(true);
   }
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_output_manager.h b/ui/ozone/platform/wayland/host/wayland_output_manager.h
index 123c45b..268d494 100644
--- a/ui/ozone/platform/wayland/host/wayland_output_manager.h
+++ b/ui/ozone/platform/wayland/host/wayland_output_manager.h
@@ -59,15 +59,7 @@
 
  private:
   // WaylandOutput::Delegate:
-  void OnOutputHandleMetrics(WaylandOutput::Id output_id,
-                             const gfx::Point& origin,
-                             const gfx::Size& logical_size,
-                             const gfx::Size& physical_size,
-                             const gfx::Insets& insets,
-                             float scale_factor,
-                             int32_t panel_transform,
-                             int32_t logical_transform,
-                             const std::string& description) override;
+  void OnOutputHandleMetrics(const WaylandOutput::Metrics& metrics) override;
 
   OutputList output_list_;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.cc b/ui/ozone/platform/wayland/host/wayland_screen.cc
index 67b1a16..b49d539a7 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -125,17 +125,9 @@
 
 WaylandScreen::~WaylandScreen() = default;
 
-void WaylandScreen::OnOutputAddedOrUpdated(WaylandOutput::Id output_id,
-                                           const gfx::Point& origin,
-                                           const gfx::Size& logical_size,
-                                           const gfx::Size& physical_size,
-                                           const gfx::Insets& insets,
-                                           float scale,
-                                           int32_t panel_transform,
-                                           int32_t logical_transform,
-                                           const std::string& label) {
-  AddOrUpdateDisplay(output_id, origin, logical_size, physical_size, insets,
-                     scale, panel_transform, logical_transform, label);
+void WaylandScreen::OnOutputAddedOrUpdated(
+    const WaylandOutput::Metrics& metrics) {
+  AddOrUpdateDisplay(metrics);
 }
 
 void WaylandScreen::OnOutputRemoved(WaylandOutput::Id output_id) {
@@ -158,53 +150,46 @@
     display_list_.RemoveDisplay(output_id);
 }
 
-void WaylandScreen::AddOrUpdateDisplay(WaylandOutput::Id output_id,
-                                       const gfx::Point& origin,
-                                       const gfx::Size& logical_size,
-                                       const gfx::Size& physical_size,
-                                       const gfx::Insets& insets,
-                                       float scale_factor,
-                                       int32_t panel_transform,
-                                       int32_t logical_transform,
-                                       const std::string& label) {
-  display::Display changed_display(output_id);
+void WaylandScreen::AddOrUpdateDisplay(const WaylandOutput::Metrics& metrics) {
+  display::Display changed_display(metrics.output_id);
 
-  DCHECK_GE(panel_transform, WL_OUTPUT_TRANSFORM_NORMAL);
-  DCHECK_LE(panel_transform, WL_OUTPUT_TRANSFORM_FLIPPED_270);
+  DCHECK_GE(metrics.panel_transform, WL_OUTPUT_TRANSFORM_NORMAL);
+  DCHECK_LE(metrics.panel_transform, WL_OUTPUT_TRANSFORM_FLIPPED_270);
   display::Display::Rotation panel_rotation =
-      WaylandTransformToRotation(panel_transform);
+      WaylandTransformToRotation(metrics.panel_transform);
   changed_display.set_panel_rotation(panel_rotation);
 
-  DCHECK_GE(logical_transform, WL_OUTPUT_TRANSFORM_NORMAL);
-  DCHECK_LE(logical_transform, WL_OUTPUT_TRANSFORM_FLIPPED_270);
+  DCHECK_GE(metrics.logical_transform, WL_OUTPUT_TRANSFORM_NORMAL);
+  DCHECK_LE(metrics.logical_transform, WL_OUTPUT_TRANSFORM_FLIPPED_270);
   display::Display::Rotation rotation =
-      WaylandTransformToRotation(logical_transform);
+      WaylandTransformToRotation(metrics.logical_transform);
   changed_display.set_rotation(rotation);
 
-  gfx::Size size_in_pixels(physical_size);
+  gfx::Size size_in_pixels(metrics.physical_size);
   if (panel_rotation == display::Display::Rotation::ROTATE_90 ||
       panel_rotation == display::Display::Rotation::ROTATE_270) {
     size_in_pixels.Transpose();
   }
   changed_display.set_size_in_pixels(size_in_pixels);
 
-  if (!logical_size.IsEmpty()) {
-    changed_display.set_bounds(gfx::Rect(origin, logical_size));
-    changed_display.SetScale(scale_factor);
+  if (!metrics.logical_size.IsEmpty()) {
+    changed_display.set_bounds(gfx::Rect(metrics.origin, metrics.logical_size));
+    changed_display.SetScale(metrics.scale_factor);
   } else {
     // Fallback to calculating using physical size.
     // This can happen if xdg_output.logical_size was not sent.
-    changed_display.SetScaleAndBounds(scale_factor, gfx::Rect(size_in_pixels));
+    changed_display.SetScaleAndBounds(metrics.scale_factor,
+                                      gfx::Rect(size_in_pixels));
     gfx::Rect new_bounds(changed_display.bounds());
-    new_bounds.set_origin(origin);
+    new_bounds.set_origin(metrics.origin);
     changed_display.set_bounds(new_bounds);
   }
-  changed_display.UpdateWorkAreaFromInsets(insets);
+  changed_display.UpdateWorkAreaFromInsets(metrics.insets);
 
   gfx::DisplayColorSpaces color_spaces;
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   auto* wayland_output =
-      connection_->wayland_output_manager()->GetOutput(output_id);
+      connection_->wayland_output_manager()->GetOutput(metrics.output_id);
   auto* color_management_output =
       wayland_output ? wayland_output->color_management_output() : nullptr;
 
@@ -241,7 +226,7 @@
       type = display::DisplayList::Type::PRIMARY;
   }
 
-  changed_display.set_label(label);
+  changed_display.set_label(metrics.description);
 
   display_list_.AddOrUpdateDisplay(changed_display, type);
 
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.h b/ui/ozone/platform/wayland/host/wayland_screen.h
index 3b2f385..37b55e34 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.h
+++ b/ui/ozone/platform/wayland/host/wayland_screen.h
@@ -19,6 +19,7 @@
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_output.h"
 #include "ui/ozone/public/platform_screen.h"
 
 namespace gfx {
@@ -41,15 +42,7 @@
   WaylandScreen& operator=(const WaylandScreen&) = delete;
   ~WaylandScreen() override;
 
-  void OnOutputAddedOrUpdated(uint32_t output_id,
-                              const gfx::Point& origin,
-                              const gfx::Size& logical_size,
-                              const gfx::Size& physical_size,
-                              const gfx::Insets& insets,
-                              float scale,
-                              int32_t panel_transform,
-                              int32_t logical_transform,
-                              const std::string& label);
+  void OnOutputAddedOrUpdated(const WaylandOutput::Metrics& metrics);
   void OnOutputRemoved(uint32_t output_id);
 
   void OnTabletStateChanged(display::TabletState tablet_state);
@@ -110,17 +103,7 @@
     bool is_suspending_ = false;
   };
 
-  // All parameters are in DIP screen coordinates/units except |physical_size|,
-  // which is in physical pixels.
-  void AddOrUpdateDisplay(uint32_t output_id,
-                          const gfx::Point& origin,
-                          const gfx::Size& logical_size,
-                          const gfx::Size& physical_size,
-                          const gfx::Insets& insets,
-                          float scale,
-                          int32_t panel_transform,
-                          int32_t logical_transform,
-                          const std::string& label);
+  void AddOrUpdateDisplay(const WaylandOutput::Metrics& metrics);
 
   raw_ptr<WaylandConnection> connection_ = nullptr;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
index 62944faa..919969b 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -321,8 +321,8 @@
   // Test with missing logical size. Should fall back to calculating from
   // physical size.
   platform_screen_->OnOutputAddedOrUpdated(
-      display_id, origin, gfx::Size(), physical_size, insets, scale,
-      panel_transform, logical_transform, "display");
+      {display_id, origin, gfx::Size(), physical_size, insets, scale,
+       panel_transform, logical_transform, "display"});
 
   const display::Display new_display(observer.GetDisplay());
   EXPECT_EQ(new_display.id(), display_id);
@@ -347,15 +347,15 @@
   display::Display display2(2, gfx::Rect(800, 0, 700, 500));
 
   platform_screen_->OnOutputAddedOrUpdated(
-      display1.id(), display1.bounds().origin(), display1.size(),
-      display1.GetSizeInPixel(), display1.GetWorkAreaInsets(),
-      display1.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
-      WL_OUTPUT_TRANSFORM_NORMAL, std::string());
+      {static_cast<uint32_t>(display1.id()), display1.bounds().origin(),
+       display1.size(), display1.GetSizeInPixel(), display1.GetWorkAreaInsets(),
+       display1.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
+       WL_OUTPUT_TRANSFORM_NORMAL, std::string()});
   platform_screen_->OnOutputAddedOrUpdated(
-      display2.id(), display2.bounds().origin(), display2.size(),
-      display2.GetSizeInPixel(), display2.GetWorkAreaInsets(),
-      display2.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
-      WL_OUTPUT_TRANSFORM_NORMAL, std::string());
+      {static_cast<uint32_t>(display2.id()), display2.bounds().origin(),
+       display2.size(), display2.GetSizeInPixel(), display2.GetWorkAreaInsets(),
+       display2.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
+       WL_OUTPUT_TRANSFORM_NORMAL, std::string()});
 
   EXPECT_EQ(platform_screen_->GetPrimaryDisplay(), display1);
 
@@ -366,15 +366,15 @@
 
   // Purposely send the output metrics out of order.
   platform_screen_->OnOutputAddedOrUpdated(
-      display2.id(), display2.bounds().origin(), display2.size(),
-      display2.GetSizeInPixel(), display2.GetWorkAreaInsets(),
-      display2.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
-      WL_OUTPUT_TRANSFORM_NORMAL, std::string());
+      {static_cast<uint32_t>(display2.id()), display2.bounds().origin(),
+       display2.size(), display2.GetSizeInPixel(), display2.GetWorkAreaInsets(),
+       display2.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
+       WL_OUTPUT_TRANSFORM_NORMAL, std::string()});
   platform_screen_->OnOutputAddedOrUpdated(
-      display1.id(), display1.bounds().origin(), display1.size(),
-      display1.GetSizeInPixel(), display1.GetWorkAreaInsets(),
-      display1.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
-      WL_OUTPUT_TRANSFORM_NORMAL, std::string());
+      {static_cast<uint32_t>(display1.id()), display1.bounds().origin(),
+       display1.size(), display1.GetSizeInPixel(), display1.GetWorkAreaInsets(),
+       display1.device_scale_factor(), WL_OUTPUT_TRANSFORM_NORMAL,
+       WL_OUTPUT_TRANSFORM_NORMAL, std::string()});
 
   EXPECT_EQ(platform_screen_->GetPrimaryDisplay(), display2);
 
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc
index bcfed7b..9dae4ce 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -30,6 +30,7 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
@@ -394,13 +395,13 @@
   // larger.
   if (GetOrientation() == TabbedPane::Orientation::kHorizontal) {
     return GetLayoutManager()->GetPreferredSize(this);
-  } else {
-    // In vertical mode, Tabstrips don't require any minimum space along their
-    // main axis, and can shrink all the way to zero size.  Only the cross axis
-    // thickness matters.
-    const gfx::Size size = GetLayoutManager()->GetPreferredSize(this);
-    return gfx::Size(size.width(), 0);
   }
+
+  // In vertical mode, Tabstrips don't require any minimum space along their
+  // main axis, and can shrink all the way to zero size.  Only the cross axis
+  // thickness matters.
+  const gfx::Size size = GetLayoutManager()->GetPreferredSize(this);
+  return gfx::Size(size.width(), 0);
 }
 
 void TabStrip::OnPaintBorder(gfx::Canvas* canvas) {
@@ -495,13 +496,24 @@
 END_METADATA
 
 TabbedPane::TabbedPane(TabbedPane::Orientation orientation,
-                       TabbedPane::TabStripStyle style) {
+                       TabbedPane::TabStripStyle style,
+                       bool scrollable) {
   DCHECK(orientation != TabbedPane::Orientation::kHorizontal ||
          style != TabbedPane::TabStripStyle::kHighlight);
   auto* layout = SetLayoutManager(std::make_unique<views::FlexLayout>());
   if (orientation == TabbedPane::Orientation::kHorizontal)
     layout->SetOrientation(views::LayoutOrientation::kVertical);
-  tab_strip_ = AddChildView(std::make_unique<TabStrip>(orientation, style));
+
+  auto tab_strip = std::make_unique<TabStrip>(orientation, style);
+  if (scrollable) {
+    scroll_view_ = AddChildView(
+        std::make_unique<ScrollView>(ScrollView::ScrollWithLayers::kEnabled));
+    tab_strip_ = tab_strip.get();
+    scroll_view_->SetContents(std::move(tab_strip));
+    scroll_view_->ClipHeightTo(0, 0);
+  } else {
+    tab_strip_ = AddChildView(std::move(tab_strip));
+  }
   contents_ = AddChildView(std::make_unique<View>());
   contents_->SetProperty(
       views::kFlexBehaviorKey,
@@ -583,6 +595,10 @@
     SelectTab(tab, animate);
 }
 
+ScrollView* TabbedPane::GetScrollView() {
+  return scroll_view_;
+}
+
 TabbedPane::Orientation TabbedPane::GetOrientation() const {
   return tab_strip_->GetOrientation();
 }
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h
index 1f7d7bf..2437c9dc 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.h
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -47,7 +47,8 @@
   };
 
   explicit TabbedPane(Orientation orientation = Orientation::kHorizontal,
-                      TabStripStyle style = TabStripStyle::kBorder);
+                      TabStripStyle style = TabStripStyle::kBorder,
+                      bool scrollable = false);
 
   TabbedPane(const TabbedPane&) = delete;
   TabbedPane& operator=(const TabbedPane&) = delete;
@@ -90,6 +91,9 @@
   // Selects |tab| (the tabstrip view, not its content) if it is valid.
   void SelectTab(TabbedPaneTab* tab, bool animate = true);
 
+  // Gets the scroll view containing the tab strip, if it exists
+  ScrollView* GetScrollView();
+
   // Gets the orientation of the tab alignment.
   Orientation GetOrientation() const;
 
@@ -137,6 +141,10 @@
   // correspond to match each TabbedPaneTab with its respective content View.
   raw_ptr<TabStrip> tab_strip_ = nullptr;
   raw_ptr<View> contents_ = nullptr;
+
+  // The scroll view containing the tab strip, if |scrollable| is specified on
+  // creation.
+  raw_ptr<ScrollView> scroll_view_ = nullptr;
 };
 
 // The tab view shown in the tab strip.
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
index 3f68fd1..df1058d 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -22,8 +23,7 @@
 
 using base::ASCIIToUTF16;
 
-namespace views {
-namespace test {
+namespace views::test {
 namespace {
 
 std::u16string DefaultTabTitle() {
@@ -74,6 +74,27 @@
   EXPECT_EQ(tabbed_pane->GetStyle(), TabbedPane::TabStripStyle::kHighlight);
 }
 
+TEST_F(TabbedPaneTest, ScrollingDisabled) {
+  auto tabbed_pane = std::make_unique<TabbedPane>(
+      TabbedPane::Orientation::kVertical, TabbedPane::TabStripStyle::kBorder);
+  EXPECT_EQ(tabbed_pane->GetScrollView(), nullptr);
+}
+
+TEST_F(TabbedPaneTest, ScrollingEnabled) {
+  auto tabbed_pane_vertical =
+      std::make_unique<TabbedPane>(TabbedPane::Orientation::kVertical,
+                                   TabbedPane::TabStripStyle::kBorder, true);
+  ASSERT_NE(tabbed_pane_vertical->GetScrollView(), nullptr);
+  EXPECT_THAT(tabbed_pane_vertical->GetScrollView(), testing::A<ScrollView*>());
+
+  auto tabbed_pane_horizontal =
+      std::make_unique<TabbedPane>(TabbedPane::Orientation::kHorizontal,
+                                   TabbedPane::TabStripStyle::kBorder, true);
+  ASSERT_NE(tabbed_pane_horizontal->GetScrollView(), nullptr);
+  EXPECT_THAT(tabbed_pane_horizontal->GetScrollView(),
+              testing::A<ScrollView*>());
+}
+
 // Tests the preferred size and layout when tabs are aligned vertically..
 TEST_F(TabbedPaneTest, SizeAndLayoutInVerticalOrientation) {
   auto tabbed_pane = std::make_unique<TabbedPane>(
@@ -368,5 +389,4 @@
   EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kSelectedChildrenChanged));
 }
 
-}  // namespace test
-}  // namespace views
+}  // namespace views::test
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index d64e918..a3c39629 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -651,7 +651,7 @@
   aura::client::SetDragDropDelegate(content_window_, this);
 
   if (params.type != Widget::InitParams::TYPE_TOOLTIP) {
-    tooltip_manager_ = std::make_unique<TooltipManagerAura>(GetWidget());
+    tooltip_manager_ = std::make_unique<TooltipManagerAura>(this);
     tooltip_controller_ = std::make_unique<corewm::TooltipController>(
         desktop_window_tree_host_->CreateTooltip(),
         wm::GetActivationClient(host_->window()));
@@ -1276,7 +1276,7 @@
   DCHECK(content_window_->IsVisible());
   if (tooltip_manager_.get())
     tooltip_manager_->UpdateTooltip();
-  TooltipManagerAura::UpdateTooltipManagerForCapture(GetWidget());
+  TooltipManagerAura::UpdateTooltipManagerForCapture(this);
   native_widget_delegate_->OnMouseEvent(event);
   // WARNING: we may have been deleted.
 }
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 0dbdbeab..9ee7b6c7 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -290,7 +290,7 @@
                            : aura::EventTargetingPolicy::kNone);
   DCHECK(GetWidget()->GetRootView());
   if (params.type != Widget::InitParams::TYPE_TOOLTIP)
-    tooltip_manager_ = std::make_unique<views::TooltipManagerAura>(GetWidget());
+    tooltip_manager_ = std::make_unique<views::TooltipManagerAura>(this);
 
   drop_helper_ = std::make_unique<DropHelper>(GetWidget()->GetRootView());
   if (params.type != Widget::InitParams::TYPE_TOOLTIP &&
@@ -1119,7 +1119,7 @@
 
   if (tooltip_manager_.get())
     tooltip_manager_->UpdateTooltip();
-  TooltipManagerAura::UpdateTooltipManagerForCapture(GetWidget());
+  TooltipManagerAura::UpdateTooltipManagerForCapture(this);
   delegate_->OnMouseEvent(event);
 }
 
diff --git a/ui/views/widget/tooltip_manager_aura.cc b/ui/views/widget/tooltip_manager_aura.cc
index 1de0108..b8e4fc8 100644
--- a/ui/views/widget/tooltip_manager_aura.cc
+++ b/ui/views/widget/tooltip_manager_aura.cc
@@ -10,6 +10,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/public/tooltip_client.h"
 
@@ -18,8 +19,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 // TooltipManagerAura public:
 
-TooltipManagerAura::TooltipManagerAura(Widget* widget)
-    : widget_(widget->AsWidget()->GetWeakPtr()) {
+TooltipManagerAura::TooltipManagerAura(
+    internal::NativeWidgetPrivate* native_widget)
+    : native_widget_(native_widget) {
   wm::SetTooltipText(GetWindow(), &tooltip_text_);
 }
 
@@ -34,7 +36,8 @@
 }
 
 // static
-void TooltipManagerAura::UpdateTooltipManagerForCapture(Widget* source) {
+void TooltipManagerAura::UpdateTooltipManagerForCapture(
+    internal::NativeWidgetPrivate* source) {
   if (!source->HasCapture())
     return;
 
@@ -61,13 +64,14 @@
   screen_position_client->ConvertPointFromScreen(target, &target_loc);
   target = target->GetEventHandlerForPoint(target_loc);
   while (target) {
-    Widget* target_widget = Widget::GetWidgetForNativeView(target);
-    if (target_widget == source)
+    internal::NativeWidgetPrivate* target_native_widget =
+        internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(target);
+    if (target_native_widget == source)
       return;
 
-    if (target_widget) {
-      if (target_widget->GetTooltipManager())
-        target_widget->GetTooltipManager()->UpdateTooltip();
+    if (target_native_widget) {
+      if (target_native_widget->GetTooltipManager())
+        target_native_widget->GetTooltipManager()->UpdateTooltip();
       return;
     }
     target = target->parent();
@@ -82,14 +86,14 @@
 }
 
 int TooltipManagerAura::GetMaxWidth(const gfx::Point& point) const {
-  return wm::GetTooltipClient(widget_->GetNativeView()->GetRootWindow())
+  return wm::GetTooltipClient(native_widget_->GetNativeView()->GetRootWindow())
       ->GetMaxWidth(point);
 }
 
 void TooltipManagerAura::UpdateTooltip() {
   aura::Window* root_window = GetWindow()->GetRootWindow();
   if (wm::GetTooltipClient(root_window)) {
-    if (!widget_->IsVisible()) {
+    if (!native_widget_->IsVisible()) {
       UpdateTooltipForTarget(nullptr, gfx::Point(), root_window);
       return;
     }
@@ -115,7 +119,10 @@
 }
 
 View* TooltipManagerAura::GetViewUnderPoint(const gfx::Point& point) {
-  View* root_view = widget_->GetRootView();
+  View* root_view = native_widget_->GetWidget()
+                        ? native_widget_->GetWidget()->GetRootView()
+                        : nullptr;
+
   if (root_view)
     return root_view->GetTooltipHandlerForPoint(point);
   return nullptr;
@@ -138,7 +145,7 @@
 }
 
 aura::Window* TooltipManagerAura::GetWindow() {
-  return widget_->GetNativeView();
+  return native_widget_->GetNativeView();
 }
 
 }  // namespace views.
diff --git a/ui/views/widget/tooltip_manager_aura.h b/ui/views/widget/tooltip_manager_aura.h
index 338b335e..5b73520 100644
--- a/ui/views/widget/tooltip_manager_aura.h
+++ b/ui/views/widget/tooltip_manager_aura.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "base/memory/weak_ptr.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/views/views_export.h"
 #include "ui/views/widget/tooltip_manager.h"
@@ -21,13 +21,13 @@
 }
 
 namespace views {
-
-class Widget;
-
+namespace internal {
+class NativeWidgetPrivate;
+}
 // TooltipManager implementation for Aura.
 class VIEWS_EXPORT TooltipManagerAura : public TooltipManager {
  public:
-  explicit TooltipManagerAura(Widget* widget);
+  explicit TooltipManagerAura(internal::NativeWidgetPrivate* native_widget);
 
   TooltipManagerAura(const TooltipManagerAura&) = delete;
   TooltipManagerAura& operator=(const TooltipManagerAura&) = delete;
@@ -38,7 +38,8 @@
   // UpdateTooltip() on it's TooltipManager. This is necessary as when capture
   // is held mouse events are only delivered to the Window that has capture even
   // though we may show tooltips for the Window under the mouse.
-  static void UpdateTooltipManagerForCapture(Widget* source);
+  static void UpdateTooltipManagerForCapture(
+      internal::NativeWidgetPrivate* source);
 
   // Returns the FontList used by all TooltipManagerAuras.
   static const gfx::FontList& GetDefaultFontList();
@@ -58,7 +59,7 @@
   // Returns the Window the tooltip text is installed on.
   aura::Window* GetWindow();
 
-  base::WeakPtr<Widget> widget_;
+  base::raw_ptr<internal::NativeWidgetPrivate> native_widget_;
   std::u16string tooltip_text_;
 };