diff --git a/AUTHORS b/AUTHORS
index 673f623..19813f9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -134,6 +134,7 @@
 Arnaud Renevier <a.renevier@samsung.com>
 Arpita Bahuguna <a.bah@samsung.com>
 Arthur Lussos <developer0420@gmail.com>
+Artur Akerberg <artur.aker@gmail.com>
 Arun Kulkarni <kulkarni.a@samsung.com>
 Arun Kumar <arun87.kumar@samsung.com>
 Arun Mankuzhi <arun.m@samsung.com>
diff --git a/DEPS b/DEPS
index 3aa9f17..f1a01093 100644
--- a/DEPS
+++ b/DEPS
@@ -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': '17844bd46186e851e100bb6138f684dd3533b203',
+  'angle_revision': '968041b54770af8917001d8fe9b52a881cfed0b2',
   # 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': 'eba023f7da110e15eaabd622b7ab2b3abf92c222',
+  'devtools_frontend_revision': 'f25ab505eddf47a6019454b4d9255e28846e9768',
   # 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.
@@ -422,7 +422,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '29fb8f8eef24ea3c80d3c2545a07e798e7f8e199',
+  'dawn_revision': '79195ca42a7712ce671faff3bc9f96010874cbd3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -493,7 +493,7 @@
   'libcxx_revision':       'e6caea47f873bd039e10e3e178112225a0d959fa',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:b9c6c19be95a3863e02f00f1fe403b2502e345b6',
+  'gn_version': 'git_revision:57c352b2b03461c24b19c678c61d7aeacc6981f4',
 
   # ninja CIPD package version.
   # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja
@@ -689,7 +689,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chrome_win_x86',
-          'version': '_YCMx6KK9JLuRq3i2A1hNXPeWPs3xx7DU9pKGBHaY9MC',
+          'version': 'iMYoiq49t4qre6etDOXQET1gjrwC9xMhs9m--HmN_BcC',
         },
       ],
   },
@@ -700,7 +700,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chrome_win_x86_64',
-          'version': 'B219feMqLtKlWnB8ll-PE7Dyk9odtx1_hMshE5e6lWgC',
+          'version': 'Liev6BKe_ERhvWxeHsdGnukHRDnzyEOqgI-CbnzXs6EC',
         },
       ],
   },
@@ -735,7 +735,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_win_x86',
-          'version': 'IC4eBusb77p8RW3eBAhJ2LQzPffPuV6lqwqm7NwxNSkC',
+          'version': 'uLIsD3x-_GZpwpuGWM6PuWwmwN80qOL45hdKj_h8xuwC',
         },
       ],
   },
@@ -746,7 +746,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_win_x86_64',
-          'version': 'Ea-jhu4lipIfpqmOJBH1nfruzXa0ETDXcjHa7NP-5PcC',
+          'version': 'dEsIuEXK6nXcXwcUVo9jTqEaWQLym2Wq2s52vf6t24kC',
         },
       ],
   },
@@ -818,12 +818,12 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    '0db905ab4301f1134a31d4a8e5cdeb687d4655db',
+    'd28954406a0222033ce50c874b5a8714ddbce0bf',
     'condition': 'checkout_android and checkout_src_internal and not checkout_clank_via_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '7da061134f35c390ac1549a82704a1762f9a5261',
+    'url': Var('chromium_git') + '/website.git' + '@' + '55059cb5bf8fb148468c278fa3757a6b97327000',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1188,7 +1188,7 @@
   },
 
   'src/third_party/cast_core/public/src':
-    Var('chromium_git') + '/cast_core/public' + '@' + '469e045e514c09701ab674d023cbaa6562866f83',
+    Var('chromium_git') + '/cast_core/public' + '@' + 'f4628fda1b370eb238ae69545024d256ca62d719',
 
   'src/third_party/catapult':
     Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'),
@@ -1245,13 +1245,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '77e64ae61ebda9e20f1eee461149e601c65b0a8f',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c950858a721bfa70ac7b4aa3faefeb42f3a3937a',
 
   'src/third_party/devtools-frontend/src':
     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' + '@' + '9bc1eda7e810110b2330f19e1a0bc8d8e10b61c4',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '9f4da9c57d605e427912cdcb8e52e6b0ec60fe9e',
     'condition': 'checkout_src_internal',
   },
 
@@ -1618,7 +1618,7 @@
 
   'src/third_party/nasm': {
       'url': Var('chromium_git') + '/chromium/deps/nasm.git' + '@' +
-      '9215e8e1d0fe474ffd3e16c1a07a0f97089e6224'
+      '5fd9246276814b5ebb7e56b821cedeecab9b674d'
   },
 
   'src/third_party/neon_2_sse/src':
@@ -1811,7 +1811,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@af6c033bca19f310f52aff91426778e2e3f6d13c',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@ba840d111dc5ed6b9f6316ee15bc44b5755b695d',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1851,7 +1851,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '874525f8b8efd7256affd396fb93c2f141cb7793',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'f0e65bab0e6cce9cb2453f703f7561c080fbcfd0',
+    Var('webrtc_git') + '/src.git' + '@' + 'a261e72bc08539d8e1975b036d1b3c1e56ce2ce9',
 
   # 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.
@@ -1921,7 +1921,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c23db719ee0ad2ed982edf499859de27a606b3d7',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@927258d8869581f5f8c7e24c17234ecec34b0864',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index b76d98d..7135a3e 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -196,20 +196,6 @@
       .Intersects(view->GetBoundsInScreen());
 }
 
-class TestStartPageSearchResult : public TestSearchResult {
- public:
-  TestStartPageSearchResult() {
-    set_display_type(ash::SearchResultDisplayType::kChip);
-    set_is_recommendation(true);
-  }
-
-  TestStartPageSearchResult(const TestStartPageSearchResult&) = delete;
-  TestStartPageSearchResult& operator=(const TestStartPageSearchResult&) =
-      delete;
-
-  ~TestStartPageSearchResult() override = default;
-};
-
 class AppListViewTest : public views::ViewsTestBase {
  public:
   AppListViewTest() = default;
@@ -590,17 +576,11 @@
     Show();
     test_api_ = std::make_unique<AppsGridViewTestApi>(apps_grid_view());
 
-    // Add suggestion apps, a folder with apps and other app list items.
-    const int kSuggestionAppNum = 3;
+    // Add a folder with apps and other app list items.
     const int kItemNumInFolder = 25;
     const int kAppListItemNum =
         SharedAppListConfig::instance().GetMaxNumOfItemsPerPage() + 1;
     AppListTestModel* model = delegate_->GetTestModel();
-    SearchModel* search_model = GetSearchModel();
-    for (size_t i = 0; i < kSuggestionAppNum; i++) {
-      search_model->results()->Add(
-          std::make_unique<TestStartPageSearchResult>());
-    }
     AppListFolderItem* folder_item =
         model->CreateAndPopulateFolderWithApps(kItemNumInFolder);
     model->PopulateApps(kAppListItemNum);
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index b18d586..eb9e8c8 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -809,6 +809,16 @@
   OnVideoRecordCountDownFinished();
 }
 
+void CaptureModeController::MaybeRestoreCachedCaptureConfigurations() {
+  if (!cached_normal_session_configs_)
+    return;
+
+  type_ = cached_normal_session_configs_->type;
+  source_ = cached_normal_session_configs_->source;
+  enable_audio_recording_ = cached_normal_session_configs_->audio_on;
+  cached_normal_session_configs_.reset();
+}
+
 void CaptureModeController::PushNewRootSizeToRecordingService(
     const gfx::Size& root_size,
     float device_scale_factor) {
@@ -1462,6 +1472,11 @@
   LaunchRecordingServiceAndStartRecording(capture_params,
                                           std::move(cursor_overlay_receiver));
 
+  // Restore the capture mode configurations that include the `type_`, `source_`
+  // and `enable_audio_recording_` after projector-inititated recording starts
+  // if any of them was overridden in projector-initiated capture mode session.
+  MaybeRestoreCachedCaptureConfigurations();
+
   capture_mode_util::SetStopRecordingButtonVisibility(
       capture_params.window->GetRootWindow(), true);
 
@@ -1641,8 +1656,12 @@
            "capture is disabled by policy.";
 
     for_projector = true;
-    // TODO(afakhry): Discuss with PM whether we want this to affect the audio
-    // settings of future generic capture mode sessions.
+
+    // Cache the normal capture mode configurations that will be used for
+    // restoration when switching to the normal capture mode session if needed.
+    cached_normal_session_configs_ =
+        CaptureSessionConfigs{type_, source_, enable_audio_recording_};
+
     enable_audio_recording_ = true;
     SetType(CaptureModeType::kVideo);
     SetSource(CaptureModeSource::kFullscreen);
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index c202c2023..ee3ec6d39 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -253,6 +253,11 @@
   // video recording right away for testing purposes.
   void StartVideoRecordingImmediatelyForTesting();
 
+  // Restores the capture mode configurations that include the `type_`,
+  // `source_` and `enable_audio_recording_` if any of them gets overridden in
+  // the projector-initiated capture mode session.
+  void MaybeRestoreCachedCaptureConfigurations();
+
   CaptureModeDelegate* delegate_for_testing() const { return delegate_.get(); }
   VideoRecordingWatcher* video_recording_watcher_for_testing() const {
     return video_recording_watcher_.get();
@@ -262,6 +267,15 @@
   friend class CaptureModeTestApi;
   friend class VideoRecordingWatcher;
 
+  // Contains the cached normal capture mode configurations that will be used
+  // for configurations restoration when switching from the projector-initiated
+  // capture mode session if needed.
+  struct CaptureSessionConfigs {
+    CaptureModeType type;
+    CaptureModeSource source;
+    bool audio_on;
+  };
+
   // Called by |video_recording_watcher_| when the display on which recording is
   // happening changes its bounds such as on display rotation or device scale
   // factor changes. In this case we push the new |root_size| in DIPs, and the
@@ -565,6 +579,8 @@
   // True in the scope of BeginVideoRecording().
   bool is_initializing_recording_ = false;
 
+  absl::optional<CaptureSessionConfigs> cached_normal_session_configs_;
+
   base::WeakPtrFactory<CaptureModeController> weak_ptr_factory_{this};
 };
 
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index dca4ec5..3a26922 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -730,6 +730,12 @@
       // settings of the normal capture mode session being overridden by the
       // projector-initiated capture mode session.
       controller_->camera_controller()->MaybeRevertAutoCameraSelection();
+
+      // Restore the capture mode configurations that include the `type_`,
+      // `source_` and `enable_audio_recording_` when the projector-initiated
+      // session ends without starting a new recording, in case any of them were
+      // overwritten by projector.
+      controller_->MaybeRestoreCachedCaptureConfigurations();
     }
   }
 
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 0298ec8..e5af25df 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -5425,6 +5425,56 @@
                   NewScreencastPreconditionState::kEnabled, {})));
 }
 
+// Tests that the capture mode configurations in normal capture mode session
+// that include the capture mode type, capture mode source and capture mode
+// audio settings will not be overridden by the projector-initiated capture mode
+// session.
+TEST_F(ProjectorCaptureModeIntegrationTests,
+       RestoreCaptureSessionConfigurationsInNormalCaptureSession) {
+  // Start an image capture mode session in window mode.
+  auto* controller =
+      StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kImage);
+
+  // Stop the normal capture mode session and start a new projector-initated
+  // capture mode session. By default the session will be of type video and in
+  // fullscreen mode with audio on.
+  controller->Stop();
+  StartProjectorModeSession();
+  EXPECT_TRUE(controller->IsActive());
+  EXPECT_EQ(controller->type(), CaptureModeType::kVideo);
+  EXPECT_EQ(controller->source(), CaptureModeSource::kFullscreen);
+  EXPECT_TRUE(controller->GetAudioRecordingEnabled());
+
+  // Stop the projector-initiated capture mode session and the original capture
+  // mode configurations will be restored.
+  controller->Stop();
+  EXPECT_EQ(controller->type(), CaptureModeType::kImage);
+  EXPECT_EQ(controller->source(), CaptureModeSource::kWindow);
+  EXPECT_FALSE(controller->GetAudioRecordingEnabled());
+
+  // Start a new projector-initiated capture mode session and start the region
+  // recording.
+  StartProjectorModeSession();
+  controller->SetSource(CaptureModeSource::kRegion);
+  CaptureModeTestApi test_api;
+  test_api.SetUserSelectedRegion(gfx::Rect(100, 100, 200, 200));
+  test_api.PerformCapture();
+  WaitForSeconds(1);
+
+  // Start another capture mode session and the source should be restored as
+  // what has been set before the projector-initiated capture mode session.
+  controller->Start(CaptureModeEntryType::kQuickSettings);
+  EXPECT_EQ(controller->source(), CaptureModeSource::kWindow);
+  controller->Stop();
+  test_api.StopVideoRecording();
+
+  // After completing the video recording in projector-initiated capture mode
+  // session, the capture mode configurations will be restored as what has been
+  // set before the projector-initiated capture mode session.
+  EXPECT_EQ(controller->type(), CaptureModeType::kImage);
+  EXPECT_FALSE(controller->GetAudioRecordingEnabled());
+}
+
 namespace {
 
 enum AbortReason {
diff --git a/ash/components/arc/disk_quota/arc_disk_quota_bridge.cc b/ash/components/arc/disk_quota/arc_disk_quota_bridge.cc
index 0a96afc..cd1cab08 100644
--- a/ash/components/arc/disk_quota/arc_disk_quota_bridge.cc
+++ b/ash/components/arc/disk_quota/arc_disk_quota_bridge.cc
@@ -149,8 +149,9 @@
 
 void ArcDiskQuotaBridge::GetFreeDiskSpace(GetFreeDiskSpaceCallback callback) {
   ash::SpacedClient::Get()->GetFreeDiskSpace(
-      "/home", base::BindOnce(&ArcDiskQuotaBridge::OnGetFreeDiskSpace,
-                              weak_factory_.GetWeakPtr(), std::move(callback)));
+      "/home/chronos/user",
+      base::BindOnce(&ArcDiskQuotaBridge::OnGetFreeDiskSpace,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void ArcDiskQuotaBridge::OnGetFreeDiskSpace(GetFreeDiskSpaceCallback callback,
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index 803c6853..da444c1 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -58,6 +58,7 @@
     "//chromeos/ash/components/dbus/dlcservice",
     "//chromeos/ash/components/dbus/dlcservice:dlcservice_proto",
     "//chromeos/ash/components/dbus/session_manager",
+    "//chromeos/ash/components/dbus/spaced:spaced",
     "//chromeos/ash/components/dbus/upstart",
     "//chromeos/ash/components/memory:memory",
     "//chromeos/components/sensors:buildflags",
diff --git a/ash/components/arc/session/arc_session_impl.cc b/ash/components/arc/session/arc_session_impl.cc
index 6da48f4..148e679 100644
--- a/ash/components/arc/session/arc_session_impl.cc
+++ b/ash/components/arc/session/arc_session_impl.cc
@@ -32,6 +32,7 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_restrictions.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "chromeos/ash/components/memory/memory.h"
 #include "chromeos/system/scheduler_configuration_manager_base.h"
 #include "components/user_manager/user_manager.h"
@@ -248,11 +249,8 @@
 
 void ArcSessionDelegateImpl::GetFreeDiskSpace(
     GetFreeDiskSpaceCallback callback) {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
-                     base::FilePath("/home")),
-      std::move(callback));
+  ash::SpacedClient::Get()->GetFreeDiskSpace("/home/chronos/user",
+                                             std::move(callback));
 }
 
 version_info::Channel ArcSessionDelegateImpl::GetChannel() {
@@ -587,13 +585,13 @@
                                              weak_factory_.GetWeakPtr()));
 }
 
-void ArcSessionImpl::OnFreeDiskSpace(int64_t space) {
+void ArcSessionImpl::OnFreeDiskSpace(absl::optional<int64_t> space) {
   // Ensure there's sufficient space on disk for the container.
-  if (space == -1) {
+  if (!space.has_value()) {
     LOG(ERROR) << "Could not determine free disk space";
     StopArcInstance(/*on_shutdown=*/false, /*should_backup_log=*/false);
     return;
-  } else if (space < kMinimumFreeDiskSpaceBytes) {
+  } else if (space.value() < kMinimumFreeDiskSpaceBytes) {
     VLOG(1) << "There is not enough disk space to start the ARC container";
     insufficient_disk_space_ = true;
     StopArcInstance(/*on_shutdown=*/false, /*should_backup_log=*/false);
diff --git a/ash/components/arc/session/arc_session_impl.h b/ash/components/arc/session/arc_session_impl.h
index 69b5a84..e7a85b5 100644
--- a/ash/components/arc/session/arc_session_impl.h
+++ b/ash/components/arc/session/arc_session_impl.h
@@ -150,7 +150,8 @@
                                        ConnectMojoCallback callback) = 0;
 
     // Gets the available disk space under /home. The result is in bytes.
-    using GetFreeDiskSpaceCallback = base::OnceCallback<void(int64_t)>;
+    using GetFreeDiskSpaceCallback =
+        base::OnceCallback<void(absl::optional<int64_t>)>;
     virtual void GetFreeDiskSpace(GetFreeDiskSpaceCallback callback) = 0;
 
     // Returns the channel for the installation.
@@ -248,7 +249,7 @@
   void DoStartMiniInstance(size_t num_cores_disabled);
 
   // Free disk space under /home in bytes.
-  void OnFreeDiskSpace(int64_t space);
+  void OnFreeDiskSpace(absl::optional<int64_t> space);
 
   // Whether adb sideloading can be changed
   void OnCanChangeAdbSideloading(bool can_change_adb_sideloading);
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 98cb0f5..1d66aed9 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -839,11 +839,6 @@
              "ExoHapticFeedbackSupport",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Enable or disable bubble showing when an application gains any UI lock.
-BASE_FEATURE(kExoLockNotification,
-             "ExoLockNotification",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enable or disable use of ordinal (unaccelerated) motion by Exo clients.
 BASE_FEATURE(kExoOrdinalMotion,
              "ExoOrdinalMotion",
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 2a7f5883..51655290 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -254,7 +254,6 @@
 BASE_DECLARE_FEATURE(kEnforceAshExtensionKeeplist);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kEolWarningNotifications);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kExoHapticFeedbackSupport);
-COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kExoLockNotification);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kExoOrdinalMotion);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kExperimentalRgbKeyboardPatterns);
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
index b2e4b51..bdcfd506 100644
--- a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
@@ -20,6 +20,7 @@
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/quick_settings_metrics_util.h"
+#include "base/check.h"
 #include "base/i18n/rtl.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkScalar.h"
@@ -35,6 +36,7 @@
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/view.h"
+#include "ui/views/view_utils.h"
 
 namespace ash {
 
@@ -210,7 +212,8 @@
                   ->ShowChannelInfoAdditionalDetails();
             }),
             channel_indicator_utils::GetFullReleaseTrackString(channel)),
-        channel_(channel) {
+        channel_(channel),
+        allow_user_feedback_(allow_user_feedback) {
     SetID(VIEW_ID_QS_VERSION_BUTTON);
     SetFlipCanvasOnPaintForRTLUI(true);
     const auto& content_corners =
@@ -218,12 +221,6 @@
     std::copy(content_corners, content_corners + kNumVersionButtonCornerRadii,
               content_corners_);
     if (features::IsQsRevampEnabled()) {
-      if (allow_user_feedback) {
-        // Visually center the label by adding an empty border on the left side
-        // that is the same width as the feedback button.
-        SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
-            0, kButtonSpacingRevamp + kSubmitFeedbackButtonRevampWidth, 0, 0)));
-      }
       SetHorizontalAlignment(gfx::ALIGN_CENTER);
       SetMinSize(gfx::Size(0, kVersionButtonRevampHeight));
     } else {
@@ -240,6 +237,19 @@
   VersionButton& operator=(const VersionButton&) = delete;
   ~VersionButton() override = default;
 
+  void SetNarrowLayout(bool narrow) {
+    DCHECK(features::IsQsRevampEnabled());
+    if (allow_user_feedback_ && !narrow) {
+      // Visually center the label by adding an empty border on the left side
+      // that is the same width as the feedback button on the right.
+      SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
+          0, kButtonSpacingRevamp + kSubmitFeedbackButtonRevampWidth, 0, 0)));
+    } else {
+      // No special centering.
+      SetBorder(nullptr);
+    }
+  }
+
   // views::LabelButton:
   void PaintButtonContents(gfx::Canvas* canvas) override {
     cc::PaintFlags flags;
@@ -281,6 +291,9 @@
   // The channel itself, BETA, DEV, or CANARY.
   const version_info::Channel channel_;
 
+  // Whether the user is allowed to send feedback.
+  const bool allow_user_feedback_;
+
   // Array of values that represents the content rounded rect corners.
   SkScalar content_corners_[kNumVersionButtonCornerRadii];
 };
@@ -415,4 +428,9 @@
   }
 }
 
+void ChannelIndicatorQuickSettingsView::SetNarrowLayout(bool narrow) {
+  DCHECK(views::IsViewClass<VersionButton>(version_button_));
+  views::AsViewClass<VersionButton>(version_button_)->SetNarrowLayout(narrow);
+}
+
 }  // namespace ash
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.h b/ash/system/channel_indicator/channel_indicator_quick_settings_view.h
index 380c28c..0e01f84 100644
--- a/ash/system/channel_indicator/channel_indicator_quick_settings_view.h
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.h
@@ -23,6 +23,12 @@
       const ChannelIndicatorQuickSettingsView&) = delete;
   ~ChannelIndicatorQuickSettingsView() override = default;
 
+  // Sets a special "narrow" layout. If `narrow` is true, centers the version
+  // string in the left button. If `narrow` is false, centers the version
+  // string with respect to the combined width of the version and feedback
+  // buttons.
+  void SetNarrowLayout(bool narrow);
+
   views::View* version_button_for_test() { return version_button_; }
   views::View* feedback_button_for_test() { return feedback_button_; }
 
diff --git a/ash/system/unified/buttons.cc b/ash/system/unified/buttons.cc
index 0374865c..8e09bed 100644
--- a/ash/system/unified/buttons.cc
+++ b/ash/system/unified/buttons.cc
@@ -43,10 +43,10 @@
 namespace {
 
 // Constants used with QsRevamp.
-constexpr int kManagedStateButtonHeight = 32;
 constexpr int kManagedStateHighlightRadius = 16;
 constexpr SkScalar kManagedStateCornerRadii[] = {16, 16, 16, 16,
                                                  16, 16, 16, 16};
+constexpr auto kManagedStateBorderInsets = gfx::Insets::TLBR(0, 12, 0, 12);
 
 // Helper function for getting ContentLayerColor.
 inline SkColor GetContentLayerColor(AshColorProvider::ContentLayerType type) {
@@ -237,7 +237,9 @@
     // Image goes first.
     image_ = AddChildView(std::make_unique<views::ImageView>());
     label_ = AddChildView(std::make_unique<views::Label>());
-    layout_manager->set_minimum_cross_axis_size(kManagedStateButtonHeight);
+
+    // Inset the icon and label so they aren't too close to the rounded corners.
+    layout_manager->set_inside_border_insets(kManagedStateBorderInsets);
     layout_manager->set_main_axis_alignment(
         views::BoxLayout::MainAxisAlignment::kCenter);
   } else {
@@ -325,6 +327,11 @@
   Shell::Get()->session_controller()->RemoveObserver(this);
 }
 
+void EnterpriseManagedView::SetNarrowLayout(bool narrow) {
+  narrow_layout_ = narrow;
+  Update();
+}
+
 void EnterpriseManagedView::OnDeviceEnterpriseInfoChanged() {
   Update();
 }
@@ -374,8 +381,12 @@
     managed_string = l10n_util::GetStringFUTF16(IDS_ASH_SHORT_MANAGED_BY,
                                                 display_domain_manager);
     if (features::IsQsRevampEnabled()) {
-      // When the managed string is short it is used as the button label.
-      label()->SetText(managed_string);
+      // Narrow layout uses the string "Managed" and wide layout uses the full
+      // string "Managed by example.com".
+      label()->SetText(narrow_layout_
+                           ? l10n_util::GetStringUTF16(
+                                 IDS_ASH_ENTERPRISE_DEVICE_MANAGED_SHORT)
+                           : managed_string);
     }
   }
   SetTooltipText(managed_string);
diff --git a/ash/system/unified/buttons.h b/ash/system/unified/buttons.h
index 565ecf332..6e592810 100644
--- a/ash/system/unified/buttons.h
+++ b/ash/system/unified/buttons.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_UNIFIED_BUTTONS_H_
 #define ASH_SYSTEM_UNIFIED_BUTTONS_H_
 
+#include "ash/ash_export.h"
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/system/enterprise/enterprise_domain_observer.h"
 #include "ash/system/power/power_status.h"
@@ -110,7 +111,7 @@
 };
 
 // A base class of the views showing device management state.
-class ManagedStateView : public views::Button {
+class ASH_EXPORT ManagedStateView : public views::Button {
  public:
   METADATA_HEADER(ManagedStateView);
 
@@ -126,6 +127,8 @@
   views::Label* label() { return label_; }
 
  private:
+  friend class QuickSettingsHeaderTest;
+
   // views::Button:
   views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
   void OnThemeChanged() override;
@@ -140,9 +143,9 @@
 
 // A view that shows whether the device is enterprise managed or not. It updates
 // by observing EnterpriseDomainModel.
-class EnterpriseManagedView : public ManagedStateView,
-                              public EnterpriseDomainObserver,
-                              public SessionObserver {
+class ASH_EXPORT EnterpriseManagedView : public ManagedStateView,
+                                         public EnterpriseDomainObserver,
+                                         public SessionObserver {
  public:
   METADATA_HEADER(EnterpriseManagedView);
 
@@ -151,6 +154,10 @@
   EnterpriseManagedView& operator=(const EnterpriseManagedView&) = delete;
   ~EnterpriseManagedView() override;
 
+  // Adjusts the layout for a narrower appearance, using a shorter label for
+  // the button.
+  void SetNarrowLayout(bool narrow);
+
  private:
   // EnterpriseDomainObserver:
   void OnDeviceEnterpriseInfoChanged() override;
@@ -161,10 +168,13 @@
 
   // Updates the view visibility and displayed string.
   void Update();
+
+  // See SetNarrowLayout().
+  bool narrow_layout_ = false;
 };
 
 // A view that shows whether the user is supervised or a child.
-class SupervisedUserView : public ManagedStateView {
+class ASH_EXPORT SupervisedUserView : public ManagedStateView {
  public:
   METADATA_HEADER(SupervisedUserView);
 
diff --git a/ash/system/unified/quick_settings_header.cc b/ash/system/unified/quick_settings_header.cc
index 5f5debd..14137de 100644
--- a/ash/system/unified/quick_settings_header.cc
+++ b/ash/system/unified/quick_settings_header.cc
@@ -8,7 +8,6 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/system_tray_client.h"
-#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
@@ -27,7 +26,14 @@
 // The bottom padding is 0 so this view is flush with the feature tiles.
 constexpr auto kHeaderPadding = gfx::Insets::TLBR(16, 16, 0, 16);
 
-constexpr int kBetweenChildSpacing = 8;
+// Horizontal space between header buttons.
+constexpr int kButtonSpacing = 8;
+
+// Header button size when the button is narrow (e.g. two column layout).
+constexpr gfx::Size kNarrowButtonSize(200, 32);
+
+// Header button size when the button is wide (e.g. one column layout).
+constexpr gfx::Size kWideButtonSize(408, 32);
 
 }  // namespace
 
@@ -35,11 +41,9 @@
     UnifiedSystemTrayController* controller) {
   DCHECK(features::IsQsRevampEnabled());
 
-  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, kHeaderPadding,
-      kBetweenChildSpacing));
-  layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kStretch);
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal, kHeaderPadding,
+      kButtonSpacing));
 
   enterprise_managed_view_ =
       AddChildView(std::make_unique<EnterpriseManagedView>(controller));
@@ -59,19 +63,38 @@
                          ->IsUserFeedbackEnabled()));
   }
 
-  UpdateVisibility();
+  UpdateVisibilityAndLayout();
 }
 
 QuickSettingsHeader::~QuickSettingsHeader() = default;
 
 void QuickSettingsHeader::ChildVisibilityChanged(views::View* child) {
-  UpdateVisibility();
+  UpdateVisibilityAndLayout();
 }
 
-void QuickSettingsHeader::UpdateVisibility() {
-  bool should_show = enterprise_managed_view_->GetVisible() ||
-                     supervised_view_->GetVisible() || !!channel_view_;
-  SetVisible(should_show);
+void QuickSettingsHeader::UpdateVisibilityAndLayout() {
+  // The managed view and the supervised view are never shown together.
+  DCHECK(!enterprise_managed_view_->GetVisible() ||
+         !supervised_view_->GetVisible());
+
+  // Make `this` view visible if a child is visible.
+  bool managed_view_visible =
+      enterprise_managed_view_->GetVisible() || supervised_view_->GetVisible();
+  bool channel_view_visible = !!channel_view_;
+  SetVisible(managed_view_visible || channel_view_visible);
+
+  // Update button sizes for one column vs. two columns.
+  bool two_columns = managed_view_visible && channel_view_visible;
+  gfx::Size size = two_columns ? kNarrowButtonSize : kWideButtonSize;
+  enterprise_managed_view_->SetPreferredSize(size);
+  supervised_view_->SetPreferredSize(size);
+  if (channel_view_)
+    channel_view_->SetPreferredSize(size);
+
+  // Use custom narrow layouts when two columns are showing.
+  enterprise_managed_view_->SetNarrowLayout(two_columns);
+  if (channel_view_)
+    channel_view_->SetNarrowLayout(two_columns);
 }
 
 BEGIN_METADATA(QuickSettingsHeader, views::View)
diff --git a/ash/system/unified/quick_settings_header.h b/ash/system/unified/quick_settings_header.h
index 58480b3..470003d 100644
--- a/ash/system/unified/quick_settings_header.h
+++ b/ash/system/unified/quick_settings_header.h
@@ -17,7 +17,8 @@
 
 // The header view shown at the top of the `QuickSettingsView`. Contains an
 // optional "Managed by" button and an optional release channel indicator. Sets
-// itself invisible when its child views do not need to be shown.
+// itself invisible when its child views do not need to be shown. When both
+// buttons are shown uses a two-column side-by-side layout.
 class ASH_EXPORT QuickSettingsHeader : public views::View {
  public:
   METADATA_HEADER(QuickSettingsHeader);
@@ -36,8 +37,9 @@
 
  private:
   // Updates visibility for this view. When it has no children it sets itself
-  // invisible so it does not consume any space.
-  void UpdateVisibility();
+  // invisible so it does not consume any space. Also updates the size of the
+  // child views based on whether one or two columns are visible.
+  void UpdateVisibilityAndLayout();
 
   // Owned by views hierarchy.
   EnterpriseManagedView* enterprise_managed_view_ = nullptr;
diff --git a/ash/system/unified/quick_settings_header_unittest.cc b/ash/system/unified/quick_settings_header_unittest.cc
index 7059fef..6210da9 100644
--- a/ash/system/unified/quick_settings_header_unittest.cc
+++ b/ash/system/unified/quick_settings_header_unittest.cc
@@ -12,13 +12,17 @@
 #include "ash/shell.h"
 #include "ash/system/model/enterprise_domain_model.h"
 #include "ash/system/model/system_tray_model.h"
+#include "ash/system/unified/buttons.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test_shell_delegate.h"
+#include "base/check.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/user_manager/user_type.h"
 #include "components/version_info/channel.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/view_utils.h"
 
 namespace ash {
 namespace {
@@ -27,6 +31,8 @@
   return Shell::Get()->system_tray_model()->enterprise_domain();
 }
 
+}  // namespace
+
 class QuickSettingsHeaderTest : public NoSessionAshTestBase {
  public:
   QuickSettingsHeaderTest() {
@@ -61,10 +67,22 @@
     return header_->GetViewByID(VIEW_ID_QS_MANAGED_BUTTON);
   }
 
+  views::Label* GetManagedButtonLabel() {
+    views::View* view = GetManagedButton();
+    DCHECK(views::IsViewClass<EnterpriseManagedView>(view));
+    return views::AsViewClass<EnterpriseManagedView>(view)->label();
+  }
+
   views::View* GetSupervisedButton() {
     return header_->GetViewByID(VIEW_ID_QS_SUPERVISED_BUTTON);
   }
 
+  views::Label* GetSupervisedButtonLabel() {
+    views::View* view = GetSupervisedButton();
+    DCHECK(views::IsViewClass<SupervisedUserView>(view));
+    return views::AsViewClass<SupervisedUserView>(view)->label();
+  }
+
   base::test::ScopedFeatureList feature_list_;
   TestShellDelegate* test_shell_delegate_ = nullptr;
   scoped_refptr<UnifiedSystemTrayModel> model_;
@@ -116,6 +134,7 @@
                            ManagementDeviceMode::kChromeEnterprise});
 
   EXPECT_TRUE(GetManagedButton()->GetVisible());
+  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed by example.com");
   EXPECT_EQ(GetManagedButton()->GetTooltipText({}), u"Managed by example.com");
   EXPECT_TRUE(header_->GetVisible());
 }
@@ -129,6 +148,8 @@
                            ManagementDeviceMode::kChromeEnterprise});
 
   EXPECT_TRUE(GetManagedButton()->GetVisible());
+  // Active Directory just shows "Managed" as the button label.
+  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed");
   EXPECT_EQ(GetManagedButton()->GetTooltipText({}),
             u"This Chrome device is enterprise managed");
   EXPECT_TRUE(header_->GetVisible());
@@ -141,6 +162,7 @@
   GetEnterpriseDomainModel()->SetEnterpriseAccountDomainInfo("example.com");
 
   EXPECT_TRUE(GetManagedButton()->GetVisible());
+  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed by example.com");
   EXPECT_EQ(GetManagedButton()->GetTooltipText({}), u"Managed by example.com");
   EXPECT_TRUE(header_->GetVisible());
 }
@@ -155,6 +177,9 @@
   CreateQuickSettingsHeader();
 
   EXPECT_TRUE(GetManagedButton()->GetVisible());
+  // The label is the shorter "Managed" due to the two-column layout.
+  EXPECT_EQ(GetManagedButtonLabel()->GetText(), u"Managed");
+  EXPECT_EQ(GetManagedButton()->GetTooltipText({}), u"Managed by example.com");
   EXPECT_TRUE(header_->channel_view_for_test());
   EXPECT_TRUE(header_->GetVisible());
 }
@@ -180,10 +205,10 @@
 
   // Now the supervised user view is visible.
   EXPECT_TRUE(GetSupervisedButton()->GetVisible());
+  EXPECT_EQ(GetSupervisedButtonLabel()->GetText(), u"Supervised user");
   EXPECT_EQ(GetSupervisedButton()->GetTooltipText({}),
             u"Account managed by parent@test.com");
   EXPECT_TRUE(header_->GetVisible());
 }
 
-}  // namespace
 }  // namespace ash
diff --git a/ash/webui/diagnostics_ui/resources/touchscreen_tester.ts b/ash/webui/diagnostics_ui/resources/touchscreen_tester.ts
index 0aeb16c8..d0eb8285 100644
--- a/ash/webui/diagnostics_ui/resources/touchscreen_tester.ts
+++ b/ash/webui/diagnostics_ui/resources/touchscreen_tester.ts
@@ -162,6 +162,18 @@
     // Update the coordinates of this touch.
     this.touches.set(touchId, touchPt);
   }
+
+  /**
+   * Handle when a 'touchend' event is fired from Touch API, or an existing
+   * touch ends from evdev.
+   * @param touchId The identifier of a touch.
+   * @param touchPt The coordinates of a touch point.
+   */
+  onDrawEnd(touchId: number, touchPt: Point): void {
+    this.drawingProvider.drawTrailMark(touchPt.x, touchPt.y);
+    // This touch has ended. Remove it from the touches object.
+    this.touches.delete(touchId);
+  }
 }
 
 declare global {
diff --git a/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html b/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html
index 050b56f9..0dafd1b 100644
--- a/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html
+++ b/ash/webui/os_feedback_ui/resources/os_feedback_shared_css.html
@@ -95,10 +95,9 @@
     }
 
     cr-checkbox {
-      --cr-checkbox-label-padding-start: 0;
+      --cr-checkbox-label-padding-start: 10px;
       --cr-checkbox-unchecked-box-color: var(--cros-icon-color-primary);
       margin-inline-end: 12px;
-      padding: 2px;
     }
 
     dialog {
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.html b/ash/webui/os_feedback_ui/resources/share_data_page.html
index 0af1bbb5..629a6d1 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -127,6 +127,7 @@
   #screenshotCheckbox {
     margin-inline-end: 10px;
     margin-inline-start: 12px;
+    width: 156px;
   }
 
   #screenshotCheckLabel {
@@ -153,7 +154,7 @@
     margin: 0 0 8px 0;
   }
 
-  label {
+  .checkbox-label {
     color: var(--cros-text-color-primary);
     line-height: 20px;
   }
@@ -256,11 +257,11 @@
           <div id="screenshotContainer" class="card-frame">
             <cr-checkbox id="screenshotCheckbox"
                 disabled="[[!hasScreenshot_(screenshotUrl)]]">
+              <div id="screenshotCheckLabel" class="checkbox-label">[[i18n('attachScreenshotLabel')]]</div>
             </cr-checkbox>
-            <label id="screenshotCheckLabel">[[i18n('attachScreenshotLabel')]]</label>
             <button id="imageButton" class="focusable" on-click="handleScreenshotClick_">
               <img id="screenshotImage" src="[[screenshotUrl]]">
-             </button>
+            </button>
           </div>
           <!-- Attach a file -->
           <div id="addFileContainer" class="card-frame">
@@ -287,53 +288,56 @@
       </div>
       <!-- User consent -->
       <div id="userConsent" class="checkbox-field-container">
-        <cr-checkbox id="userConsentCheckbox" aria-labelledby="userConsentLabel"></cr-checkbox>
-        <label id="userConsentLabel">[[i18n('userConsentLabel')]]</label>
+        <cr-checkbox id="userConsentCheckbox" aria-labelledby="userConsentLabel">
+          <div id="userConsentLabel" class="checkbox-label">[[i18n('userConsentLabel')]]</div>
+        </cr-checkbox>
       </div>
       <!-- Diagnostic data -->
       <div id="shareDiagnosticData">
         <h2 id="shareDiagnosticDataLabel">[[i18n('shareDiagnosticDataLabel')]]</h2>
         <!-- URL -->
         <div id="pageUrl" class="checkbox-field-container" hidden="[[!feedbackContext.pageUrl.url]]">
-          <cr-checkbox id="pageUrlCheckbox" aria-labelledby="pageUrlLabel" checked></cr-checkbox>
-          <label id="pageUrlLabel">[[i18n('sharePageUrlLabel')]]&nbsp;
-            <a href="[[feedbackContext.pageUrl.url]]" class="overflow-text" id="pageUrlText" target="_blank">
-              [[feedbackContext.pageUrl.url]]
-            </a>
-            <paper-tooltip for="pageUrlText" fitToVisibleBounds>
-              <div id="tooltipContent">[[feedbackContext.pageUrl.url]]</div>
-            </paper-tooltip>
-          </label>
+          <cr-checkbox id="pageUrlCheckbox" aria-labelledby="pageUrlLabel" checked>
+            <div id="pageUrlLabel" class="checkbox-label">[[i18n('sharePageUrlLabel')]]&nbsp;
+              <a href="[[feedbackContext.pageUrl.url]]" class="overflow-text" id="pageUrlText" target="_blank">
+                [[feedbackContext.pageUrl.url]]
+              </a>
+              <paper-tooltip for="pageUrlText" fitToVisibleBounds>
+                <div id="tooltipContent">[[feedbackContext.pageUrl.url]]</div>
+              </paper-tooltip>
+            </div>
+          </cr-checkbox>
         </div>
         <!-- System Information -->
         <div id="sysInfoContainer" class="checkbox-field-container">
           <cr-checkbox id="sysInfoCheckbox" aria-labelledby="sysInfoCheckboxLabel" checked>
+            <div id="sysInfoCheckboxLabel" inner-h-t-m-l="[[sysInfoCheckboxLabel_]]"></div>
           </cr-checkbox>
-          <label id="sysInfoCheckboxLabel" inner-h-t-m-l="[[sysInfoCheckboxLabel_]]"></label>
         </div>
         <!-- Assistant Logs (Googler Internal Only) -->
         <div id="assistantLogsContainer" class="checkbox-field-container"
             hidden="[[!shouldShowAssistantCheckbox]]">
           <cr-checkbox id="assiatantLogsCheckbox" aria-labelledby="assistantLogsLabel" checked>
+            <div id="assistantLogsLabel" class="checkbox-label" inner-h-t-m-l="[[assistantLogsCheckboxLabel_]]"></div>
           </cr-checkbox>
-          <label id="assistantLogsLabel" inner-h-t-m-l="[[assistantLogsCheckboxLabel_]]"></label>
         </div>
         <!-- Bluetooth Logs (Googler Internal Only) -->
         <div id="bluetoothCheckboxContainer" class="checkbox-field-container"
             hidden="[[!shouldShowBluetoothCheckbox]]">
           <cr-checkbox id="bluetoothLogsCheckbox" aria-labelledby="bluetoothInfoLabel" checked>
+            <div id="bluetoothInfoLabel" class="checkbox-label" 
+                inner-h-t-m-l="[[bluetoothLogsCheckboxLabel_]]"></div>
           </cr-checkbox>
-          <label id="bluetoothInfoLabel" inner-h-t-m-l="[[bluetoothLogsCheckboxLabel_]]"></label>
         </div>
         <!-- Performance trace -->
         <div id="performanceTraceContainer" class="checkbox-field-container"
             hidden="[[!shouldShowPerformanceTraceCheckbox_(feedbackContext)]]">
           <cr-checkbox id="performanceTraceCheckbox"
               aria-labelledby="performanceTraceCheckboxLabel" checked>
+            <div id="performanceTraceCheckboxLabel" class="checkbox-label"
+                inner-h-t-m-l="[[performanceTraceCheckboxLabel_]]">
+            </div>
           </cr-checkbox>
-          <label id="performanceTraceCheckboxLabel"
-              inner-h-t-m-l="[[performanceTraceCheckboxLabel_]]">
-          </label>
         </div>
       </div>
       <!-- Privacy note -->
diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h
index 5772b68..e8865f38 100644
--- a/base/allocator/partition_allocator/partition_address_space.h
+++ b/base/allocator/partition_allocator/partition_address_space.h
@@ -204,8 +204,8 @@
   // ArrayBuffers be located inside of it.
   static constexpr size_t kRegularPoolSize = kPoolMaxSize;
   static constexpr size_t kBRPPoolSize = kPoolMaxSize;
-  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSize) &&
-                base::bits::IsPowerOfTwo(kBRPPoolSize));
+  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSize));
+  static_assert(base::bits::IsPowerOfTwo(kBRPPoolSize));
 #if defined(PA_DYNAMICALLY_SELECT_POOL_SIZE)
   // We can't afford pool sizes as large as kPoolMaxSize on Windows <8.1 (see
   // crbug.com/1101421 and crbug.com/1217759).
@@ -213,14 +213,14 @@
   static constexpr size_t kBRPPoolSizeForLegacyWindows = 4 * kGiB;
   static_assert(kRegularPoolSizeForLegacyWindows < kRegularPoolSize);
   static_assert(kBRPPoolSizeForLegacyWindows < kBRPPoolSize);
-  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForLegacyWindows) &&
-                base::bits::IsPowerOfTwo(kBRPPoolSizeForLegacyWindows));
+  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForLegacyWindows));
+  static_assert(base::bits::IsPowerOfTwo(kBRPPoolSizeForLegacyWindows));
 #endif  // defined(PA_DYNAMICALLY_SELECT_POOL_SIZE)
   static constexpr size_t kConfigurablePoolMaxSize = kPoolMaxSize;
   static constexpr size_t kConfigurablePoolMinSize = 1 * kGiB;
   static_assert(kConfigurablePoolMinSize <= kConfigurablePoolMaxSize);
-  static_assert(base::bits::IsPowerOfTwo(kConfigurablePoolMaxSize) &&
-                base::bits::IsPowerOfTwo(kConfigurablePoolMinSize));
+  static_assert(base::bits::IsPowerOfTwo(kConfigurablePoolMaxSize));
+  static_assert(base::bits::IsPowerOfTwo(kConfigurablePoolMinSize));
 
 #if BUILDFLAG(IS_IOS)
 
@@ -235,8 +235,8 @@
   static constexpr size_t kBRPPoolSizeForIOSTestProcess = kGiB / 4;
   static_assert(kRegularPoolSizeForIOSTestProcess < kRegularPoolSize);
   static_assert(kBRPPoolSizeForIOSTestProcess < kBRPPoolSize);
-  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForIOSTestProcess) &&
-                base::bits::IsPowerOfTwo(kBRPPoolSizeForIOSTestProcess));
+  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForIOSTestProcess));
+  static_assert(base::bits::IsPowerOfTwo(kBRPPoolSizeForIOSTestProcess));
 #endif  // BUILDFLAG(IOS_IOS)
 
 #if !defined(PA_DYNAMICALLY_SELECT_POOL_SIZE)
diff --git a/base/containers/buffer_iterator.h b/base/containers/buffer_iterator.h
index 2c622e8..dbb0dca 100644
--- a/base/containers/buffer_iterator.h
+++ b/base/containers/buffer_iterator.h
@@ -10,6 +10,7 @@
 #include "base/bit_cast.h"
 #include "base/containers/span.h"
 #include "base/numerics/checked_math.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 
@@ -70,14 +71,10 @@
   // position. On success, the iterator position is advanced by sizeof(T). If
   // there are not sizeof(T) bytes remaining in the buffer, returns nullptr.
   template <typename T,
-            typename =
-                typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
   T* MutableObject() {
     size_t size = sizeof(T);
-    size_t next_position;
-    if (!CheckAdd(position(), size).AssignIfValid(&next_position))
-      return nullptr;
-    if (next_position > total_size())
+    if (size > remaining_.size())
       return nullptr;
     T* t = bit_cast<T*>(remaining_.data());
     remaining_ = remaining_.subspan(size);
@@ -87,27 +84,35 @@
   // Returns a const pointer to an object of type T in the buffer at the current
   // position.
   template <typename T,
-            typename =
-                typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
   const T* Object() {
     return MutableObject<const T>();
   }
 
+  // Copies out an object. As compared to using Object, this avoids potential
+  // unaligned access which may be undefined behavior.
+  template <typename T,
+            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+  absl::optional<T> CopyObject() {
+    absl::optional<T> t;
+    if (remaining_.size() >= sizeof(T)) {
+      memcpy(&t.emplace(), remaining_.data(), sizeof(T));
+      remaining_ = remaining_.subspan(sizeof(T));
+    }
+    return t;
+  }
+
   // Returns a span of |count| T objects in the buffer at the current position.
   // On success, the iterator position is advanced by |sizeof(T) * count|. If
   // there are not enough bytes remaining in the buffer to fulfill the request,
   // returns an empty span.
   template <typename T,
-            typename =
-                typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
   span<T> MutableSpan(size_t count) {
     size_t size;
     if (!CheckMul(sizeof(T), count).AssignIfValid(&size))
       return span<T>();
-    size_t next_position;
-    if (!CheckAdd(position(), size).AssignIfValid(&next_position))
-      return span<T>();
-    if (next_position > total_size())
+    if (size > remaining_.size())
       return span<T>();
     auto result = span<T>(bit_cast<T*>(remaining_.data()), count);
     remaining_ = remaining_.subspan(size);
@@ -117,8 +122,7 @@
   // Returns a span to |count| const objects of type T in the buffer at the
   // current position.
   template <typename T,
-            typename =
-                typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
   span<const T> Span(size_t count) {
     return MutableSpan<const T>(count);
   }
@@ -126,11 +130,19 @@
   // Resets the iterator position to the absolute offset |to|.
   void Seek(size_t to) { remaining_ = buffer_.subspan(to); }
 
+  // Limits the remaining data to the specified size.
+  // Seeking to an absolute offset reverses this.
+  void TruncateTo(size_t size) { remaining_ = remaining_.first(size); }
+
   // Returns the total size of the underlying buffer.
-  size_t total_size() { return buffer_.size(); }
+  size_t total_size() const { return buffer_.size(); }
 
   // Returns the current position in the buffer.
-  size_t position() { return buffer_.size_bytes() - remaining_.size_bytes(); }
+  size_t position() const {
+    DCHECK(buffer_.data() <= remaining_.data());
+    DCHECK(remaining_.data() <= buffer_.data() + buffer_.size());
+    return static_cast<size_t>(remaining_.data() - buffer_.data());
+  }
 
  private:
   // The original buffer that the iterator was constructed with.
diff --git a/base/containers/buffer_iterator_unittest.cc b/base/containers/buffer_iterator_unittest.cc
index c341579..92ed22c7 100644
--- a/base/containers/buffer_iterator_unittest.cc
+++ b/base/containers/buffer_iterator_unittest.cc
@@ -166,5 +166,37 @@
   EXPECT_EQ(sizeof(buffer), iterator.total_size());
 }
 
+TEST(BufferIteratorTest, CopyObject) {
+  TestStruct expected = CreateTestStruct();
+
+  constexpr int kNumCopies = 3;
+  char buffer[sizeof(TestStruct) * kNumCopies];
+  for (int i = 0; i < kNumCopies; i++)
+    memcpy(buffer + i * sizeof(TestStruct), &expected, sizeof(TestStruct));
+
+  BufferIterator<char> iterator(buffer);
+  absl::optional<TestStruct> actual;
+  for (int i = 0; i < kNumCopies; i++) {
+    actual = iterator.CopyObject<TestStruct>();
+    ASSERT_TRUE(actual.has_value());
+    EXPECT_EQ(expected, *actual);
+  }
+  actual = iterator.CopyObject<TestStruct>();
+  EXPECT_FALSE(actual.has_value());
+}
+
+TEST(BufferIteratorTest, SeekWithSizeConfines) {
+  const char buffer[] = "vindicate";
+  BufferIterator<const char> iterator(buffer);
+  iterator.Seek(5);
+  iterator.TruncateTo(3);
+  EXPECT_TRUE(iterator.Span<char>(4).empty());
+
+  std::string result;
+  while (const char* c = iterator.Object<char>())
+    result += *c;
+  EXPECT_EQ(result, "cat");
+}
+
 }  // namespace
 }  // namespace base
diff --git a/base/win/process_startup_helper.cc b/base/win/process_startup_helper.cc
index cd82811..1a700e8 100644
--- a/base/win/process_startup_helper.cc
+++ b/base/win/process_startup_helper.cc
@@ -7,19 +7,13 @@
 #include <crtdbg.h>
 #include <new.h>
 
-#include <ostream>
-#include <string>
-
 #include "base/base_switches.h"
-#include "base/check.h"
 #include "base/command_line.h"
-#include "base/strings/utf_string_conversions.h"
 
 namespace {
 
-// Handlers for invalid parameter and pure call. They generate an unoptimized
-// CHECK() unconditionally to always generate file+line error messages even in
-// official builds.
+// Handlers for invalid parameter and pure call. They generate a breakpoint to
+// tell breakpad that it needs to dump the process.
 // These functions should be written to be unique in order to avoid confusing
 // call stacks from /OPT:ICF function folding. Printing a unique message or
 // returning a unique value will do this. Note that for best results they need
@@ -29,20 +23,13 @@
                       const wchar_t* file,
                       unsigned int line,
                       uintptr_t reserved) {
-  logging::CheckError::Check(base::WideToUTF8(file).c_str(),
-                             static_cast<int>(line),
-                             base::WideToUTF8(expression).c_str())
-          .stream()
-      << base::WideToUTF8(function);
+  __debugbreak();
   // Use a different exit code from PureCall to avoid COMDAT folding.
   _exit(1);
 }
 
 void PureCall() {
-  // This inlines a CHECK(false) that won't be optimized to IMMEDIATE_CRASH() in
-  // an official build and hence will set a crash key for file:line for better
-  // error reporting.
-  logging::CheckError::Check(__FILE__, __LINE__, "false");
+  __debugbreak();
   // Use a different exit code from InvalidParameter to avoid COMDAT folding.
   _exit(2);
 }
diff --git a/chrome/VERSION b/chrome/VERSION
index a3ed6ce..a6857f7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=109
 MINOR=0
-BUILD=5366
+BUILD=5367
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/res/values/colors.xml b/chrome/android/features/tab_ui/java/res/values/colors.xml
index 76a6f2fc..a725c77 100644
--- a/chrome/android/features/tab_ui/java/res/values/colors.xml
+++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -46,7 +46,4 @@
     <color name="incognito_tab_grid_dialog_ungroup_bar_text_hovered_color">@color/modern_white</color>
 
     <color name="incognito_tab_selection_editor_toolbar_bg_color">@color/toolbar_background_primary_dark</color>
-
-    <color name="incognito_tab_selection_editor_selection_action_bg_color">@color/baseline_neutral_variant_200</color>
-    <color name="incognito_tab_selection_editor_selection_action_check">@color/toolbar_background_primary_dark</color>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index e8d99be..e039ee6 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -85,4 +85,5 @@
 
     <!-- Dimens for TabSelectionEditorV2 -->
     <dimen name="tab_selection_editor_action_view_padding">14dp</dimen>
+    <dimen name="tab_selection_editor_selection_action_inset">2dp</dimen>
 </resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 8df42e0..edc913813 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -548,7 +548,7 @@
 
         if (TabUiFeatureUtilities.isTabSelectionEditorV2Enabled(mContext)) {
             List<TabSelectionEditorAction> actions = new ArrayList<>();
-            actions.add(TabSelectionEditorSelectionAction.createAction(mContext, ShowMode.IF_ROOM,
+            actions.add(TabSelectionEditorSelectionAction.createAction(mContext, ShowMode.MENU_ONLY,
                     ButtonType.ICON_AND_TEXT, IconPosition.END,
                     mTabModelSelector.getCurrentModel().isIncognito()));
             actions.add(TabSelectionEditorCloseAction.createAction(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
index 07146ded..f3350f3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
@@ -156,8 +156,11 @@
                                 ColorStateList.valueOf(Color.TRANSPARENT))
                         .with(TabSelectionEditorActionProperties.SKIP_ICON_TINT, false)
                         .with(TabSelectionEditorActionProperties.ON_CLICK_LISTENER, this::perform)
+                        .with(TabSelectionEditorActionProperties.SHOULD_DISMISS_MENU, true)
                         .with(TabSelectionEditorActionProperties.ON_SELECTION_STATE_CHANGE,
                                 this::onSelectionStateChange)
+                        .with(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU,
+                                this::onShownInMenu)
                         .build();
 
         if (contentDescriptionResourceId == null) return;
@@ -191,6 +194,8 @@
         return true;
     }
 
+    public void onShownInMenu() {}
+
     /**
      * @return Whether the TabSelectionEditor supports applying the actions to related tabs.
      */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java
index ca42ee5..b448425 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProperties.java
@@ -34,7 +34,7 @@
     public static final WritableObjectPropertyKey<String> CONTENT_DESCRIPTION =
             new WritableObjectPropertyKey<>();
     public static final WritableObjectPropertyKey<Drawable> ICON =
-            new WritableObjectPropertyKey<>();
+            new WritableObjectPropertyKey<>(true);
     public static final WritableBooleanPropertyKey ENABLED = new WritableBooleanPropertyKey();
     public static final WritableIntPropertyKey ITEM_COUNT = new WritableIntPropertyKey();
     public static final WritableObjectPropertyKey<ColorStateList> TEXT_TINT =
@@ -55,8 +55,12 @@
 
     public static final WritableObjectPropertyKey<Runnable> ON_CLICK_LISTENER =
             new WritableObjectPropertyKey<>();
+    public static final WritableBooleanPropertyKey SHOULD_DISMISS_MENU =
+            new WritableBooleanPropertyKey();
     public static final WritableObjectPropertyKey<Callback<List<Integer>>>
             ON_SELECTION_STATE_CHANGE = new WritableObjectPropertyKey<>();
+    public static final WritableObjectPropertyKey<Runnable> ON_SHOWN_IN_MENU =
+            new WritableObjectPropertyKey<>();
 
     /**
      * Keys for the {@link TabSelectionEditorAction}.
@@ -64,11 +68,11 @@
     public static final PropertyKey[] ACTION_KEYS = {MENU_ITEM_ID, SHOW_MODE, BUTTON_TYPE,
             ICON_POSITION, TITLE_RESOURCE_ID, TITLE_IS_PLURAL, CONTENT_DESCRIPTION_RESOURCE_ID,
             ICON, ENABLED, ITEM_COUNT, TEXT_TINT, ICON_TINT, SKIP_ICON_TINT, ON_CLICK_LISTENER,
-            ON_SELECTION_STATE_CHANGE};
+            SHOULD_DISMISS_MENU, ON_SELECTION_STATE_CHANGE, ON_SHOWN_IN_MENU};
 
     /**
      * Keys for the {@link TabSelectionEditorMenuItem}.
      */
-    public static final PropertyKey[] MENU_ITEM_KEYS = {
-            MENU_ITEM_ID, TITLE, CONTENT_DESCRIPTION, ICON, ENABLED, ITEM_COUNT};
+    public static final PropertyKey[] MENU_ITEM_KEYS = {MENU_ITEM_ID, TITLE, CONTENT_DESCRIPTION,
+            ICON, ICON_TINT, ENABLED, ITEM_COUNT, ON_SHOWN_IN_MENU};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java
index bf45813..0c5ebae 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionViewLayout.java
@@ -82,9 +82,12 @@
     }
 
     /**
+     * @param popupListener for handling show events.
      * @param delegate for handling menu button presses.
      */
-    public void setListMenuButtonDelegate(ListMenuButtonDelegate delegate) {
+    public void setListMenuButtonDelegate(
+            ListMenuButton.PopupMenuShownListener popupListener, ListMenuButtonDelegate delegate) {
+        mMenuButton.addPopupListener(popupListener);
         mMenuButton.setDelegate(delegate);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java
index cc751d6..0a5f607 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenu.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorActionViewLayout.ActionViewLayoutDelegate;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.widget.listmenu.ListMenu;
+import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
@@ -35,9 +36,9 @@
  * {@link TabSelectionEditorActionViewLayout} for Action views. The menu contains a list of
  * {@link TabSelectionEditorMenuItem}s which hold optional action views if room is available.
  */
-public class TabSelectionEditorMenu implements ListMenu, OnItemClickListener,
-                                               SelectionDelegate.SelectionObserver<Integer>,
-                                               ActionViewLayoutDelegate {
+public class TabSelectionEditorMenu
+        implements ListMenu, OnItemClickListener, SelectionDelegate.SelectionObserver<Integer>,
+                   ActionViewLayoutDelegate, ListMenuButton.PopupMenuShownListener {
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ListItemType.MENU_ITEM})
     public static @interface ListItemType {
@@ -74,7 +75,7 @@
         mListView.setDivider(null);
         mListView.setOnItemClickListener(this);
 
-        mActionViewLayout.setListMenuButtonDelegate(() -> this);
+        mActionViewLayout.setListMenuButtonDelegate(this, () -> this);
     }
 
     private void registerItemTypes() {
@@ -85,6 +86,13 @@
         // clang-format on
     }
 
+    @Override
+    public void onPopupMenuShown() {
+        for (ListItem listItem : mModelList) {
+            listItem.model.get(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU).run();
+        }
+    }
+
     private ListItem buildListItem(int menuItemId) {
         // Model values are populated while configuring the TabSelectionEditorMenuItem.
         return new ListItem(ListItemType.MENU_ITEM,
@@ -156,7 +164,7 @@
 
         if (!item.onClick()) return;
 
-        mActionViewLayout.dismissMenu();
+        if (item.shouldDismissMenu()) mActionViewLayout.dismissMenu();
     }
 
     /**
@@ -164,6 +172,17 @@
      */
     @Override
     public void setVisibleActionViews(Set<TabSelectionEditorMenuItem> visibleActions) {
+        if (mModelList.size() == visibleActions.size()) {
+            boolean unchanged = true;
+            for (TabSelectionEditorMenuItem item : visibleActions) {
+                if (mModelList.indexOf(item.getListItem()) == -1) {
+                    unchanged = false;
+                    break;
+                }
+            }
+            if (unchanged) return;
+        }
+
         // Reset the entire list to maintain the correct ordering.
         mModelList.clear();
         for (TabSelectionEditorMenuItem item : mMenuItems.values()) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java
index 7175171..28f3406 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuAdapter.java
@@ -4,12 +4,11 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import android.content.res.ColorStateList;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import androidx.appcompat.content.res.AppCompatResources;
-
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.ListModelChangeProcessor;
@@ -104,9 +103,15 @@
         } else if (key == TabSelectionEditorActionProperties.ON_CLICK_LISTENER) {
             menuItem.setOnClickListener(
                     actionModel.get(TabSelectionEditorActionProperties.ON_CLICK_LISTENER));
+        } else if (key == TabSelectionEditorActionProperties.SHOULD_DISMISS_MENU) {
+            menuItem.setShouldDismissMenu(
+                    actionModel.get(TabSelectionEditorActionProperties.SHOULD_DISMISS_MENU));
         } else if (key == TabSelectionEditorActionProperties.ON_SELECTION_STATE_CHANGE) {
             menuItem.setOnSelectionStateChange(
                     actionModel.get(TabSelectionEditorActionProperties.ON_SELECTION_STATE_CHANGE));
+        } else if (key == TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU) {
+            menuItem.setOnShownInMenu(
+                    actionModel.get(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU));
         }
     }
 
@@ -140,10 +145,6 @@
                     view.getResources().getDimensionPixelOffset(R.dimen.menu_padding_start),
                     textView.getPaddingTop(), textView.getPaddingEnd(),
                     textView.getPaddingBottom());
-            // Use default color for menu icon.
-            ApiCompatibilityUtils.setImageTintList(startIcon,
-                    AppCompatResources.getColorStateList(
-                            view.getContext(), R.color.default_icon_color_secondary_tint_list));
             startIcon.setVisibility(View.VISIBLE);
             endIcon.setVisibility(View.GONE);
         } else if (propertyKey == TabSelectionEditorActionProperties.ENABLED) {
@@ -151,6 +152,11 @@
             textView.setEnabled(model.get(TabSelectionEditorActionProperties.ENABLED));
             startIcon.setEnabled(model.get(TabSelectionEditorActionProperties.ENABLED));
             endIcon.setEnabled(model.get(TabSelectionEditorActionProperties.ENABLED));
+        } else if (propertyKey == TabSelectionEditorActionProperties.ICON_TINT) {
+            ColorStateList colorStateList = model.get(TabSelectionEditorActionProperties.ICON_TINT);
+            if (colorStateList != null) {
+                ApiCompatibilityUtils.setImageTintList(startIcon, colorStateList);
+            }
         }
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java
index fd846e8..f8bfaa5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuItem.java
@@ -13,6 +13,7 @@
 import android.widget.Button;
 
 import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.widget.TextViewCompat;
 
 import org.chromium.base.Callback;
@@ -37,6 +38,7 @@
     private boolean mShowText;
     private boolean mShowIcon;
     private boolean mEnabled;
+    private boolean mShouldDismissMenu;
     private boolean mActionViewShowing;
     private ColorStateList mIconTint;
 
@@ -155,10 +157,17 @@
         // A null colorStateList is used with TabSelectionEditorActionProperties.SKIP_ICON_TINT
         // = true to signal that a custom tint is used. Ignore null so that this custom tint is
         // not overridden.
-        if (colorStateList == null) return;
+        if (colorStateList == null) {
+            mIconTint = null;
+            mListItem.model.set(TabSelectionEditorActionProperties.ICON_TINT, null);
+            return;
+        }
 
         // mListItem uses the default icon tint whenever shown. Cache the tint to restore it when
         // the action view shown state is toggled.
+        mListItem.model.set(TabSelectionEditorActionProperties.ICON_TINT,
+                AppCompatResources.getColorStateList(
+                        mContext, R.color.default_icon_color_secondary_tint_list));
         mIconTint = colorStateList;
         if (mActionView != null && mActionViewShowing) {
             TextViewCompat.setCompoundDrawableTintList(mActionView, colorStateList);
@@ -180,10 +189,22 @@
         }
     }
 
+    public void setShouldDismissMenu(boolean shouldDismissMenu) {
+        mShouldDismissMenu = shouldDismissMenu;
+    }
+
+    public boolean shouldDismissMenu() {
+        return mShouldDismissMenu;
+    }
+
     public void setOnSelectionStateChange(Callback<List<Integer>> callback) {
         mOnSelectionStateChange = callback;
     }
 
+    public void setOnShownInMenu(Runnable runnable) {
+        mListItem.model.set(TabSelectionEditorActionProperties.ON_SHOWN_IN_MENU, runnable);
+    }
+
     /**
      * Handler for click events on the menu item or action view.
      */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
index 09b5836..a664741e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.LayerDrawable;
 
 import androidx.annotation.IntDef;
@@ -65,6 +66,7 @@
         mActionState = ActionState.UNKNOWN;
         getPropertyModel().set(TabSelectionEditorActionProperties.ICON_TINT, null);
         getPropertyModel().set(TabSelectionEditorActionProperties.SKIP_ICON_TINT, true);
+        getPropertyModel().set(TabSelectionEditorActionProperties.SHOULD_DISMISS_MENU, false);
         updateState(ActionState.SELECT_ALL, isIncognito);
     }
 
@@ -74,6 +76,11 @@
     }
 
     @Override
+    public void onShownInMenu() {
+        updateDrawable();
+    }
+
+    @Override
     public void onSelectionStateChange(List<Integer> tabIds) {
         setEnabledAndItemCount(true, tabIds.size());
         updateState(getActionDelegate().areAllTabsSelected() ? ActionState.DESELECT_ALL
@@ -110,29 +117,42 @@
         if (mActionState == ActionState.SELECT_ALL) {
             getPropertyModel().set(TabSelectionEditorActionProperties.TITLE_RESOURCE_ID,
                     R.string.tab_selection_editor_select_all);
-            layers.getDrawable(BACKGROUND)
-                    .setLevel(
-                            mContext.getResources().getInteger(R.integer.list_item_level_default));
-            layers.getDrawable(BACKGROUND)
-                    .setTint(TabUiThemeProvider.getSelectionActionIconBackgroundColor(
-                            mContext, isIncognito));
-
-            layers.getDrawable(CHECKMARK).setAlpha(0);
-            layers.getDrawable(CHECKMARK).setTint(Color.TRANSPARENT);
+            updateDrawable();
         } else if (mActionState == ActionState.DESELECT_ALL) {
             getPropertyModel().set(TabSelectionEditorActionProperties.TITLE_RESOURCE_ID,
                     R.string.tab_selection_editor_deselect_all);
+            updateDrawable();
+        } else {
+            assert false : "Invalid selection state";
+        }
+    }
+
+    private void updateDrawable() {
+        LayerDrawable layers =
+                (LayerDrawable) getPropertyModel().get(TabSelectionEditorActionProperties.ICON);
+        if (mActionState == ActionState.SELECT_ALL) {
+            layers.getDrawable(BACKGROUND)
+                    .setLevel(
+                            mContext.getResources().getInteger(R.integer.list_item_level_default));
+
+            layers.setDrawable(CHECKMARK,
+                    AnimatedVectorDrawableCompat.create(
+                            mContext, R.drawable.ic_check_googblue_20dp_animated));
+            layers.getDrawable(CHECKMARK).setAlpha(0);
+            layers.getDrawable(CHECKMARK).setTint(Color.TRANSPARENT);
+            getPropertyModel().set(TabSelectionEditorActionProperties.ICON, layers);
+        } else if (mActionState == ActionState.DESELECT_ALL) {
             layers.getDrawable(BACKGROUND)
                     .setLevel(
                             mContext.getResources().getInteger(R.integer.list_item_level_selected));
-            layers.getDrawable(BACKGROUND)
-                    .setTint(TabUiThemeProvider.getSelectionActionIconBackgroundColor(
-                            mContext, isIncognito));
 
+            layers.setDrawable(CHECKMARK,
+                    AnimatedVectorDrawableCompat.create(
+                            mContext, R.drawable.ic_check_googblue_20dp_animated));
             layers.getDrawable(CHECKMARK).setAlpha(255);
             layers.getDrawable(CHECKMARK).setTint(
-                    TabUiThemeProvider.getSelectionActionIconCheckedDrawableColor(
-                            mContext, isIncognito));
+                    TabUiThemeProvider.getSelectionActionIconCheckedDrawableColor(mContext));
+            getPropertyModel().set(TabSelectionEditorActionProperties.ICON, layers);
             ((AnimatedVectorDrawableCompat) layers.getDrawable(CHECKMARK)).start();
         } else {
             assert false : "Invalid selection state";
@@ -142,9 +162,13 @@
     private static Drawable buildDrawable(Context context) {
         Drawable[] drawables = new Drawable[2];
 
-        drawables[BACKGROUND] = ResourcesCompat.getDrawable(context.getResources(),
+        Drawable selectionListIcon = ResourcesCompat.getDrawable(context.getResources(),
                 R.drawable.tab_grid_selection_list_icon, context.getTheme());
-
+        drawables[BACKGROUND] = new InsetDrawable(selectionListIcon,
+                (int) context.getResources().getDimension(
+                        R.dimen.tab_selection_editor_selection_action_inset));
+        drawables[BACKGROUND].setTint(
+                TabUiThemeProvider.getSelectionActionIconBackgroundColor(context));
         drawables[CHECKMARK] = AnimatedVectorDrawableCompat.create(
                 context, R.drawable.ic_check_googblue_20dp_animated);
         return new LayerDrawable(drawables);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 5604aaa..a1c32de 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -527,7 +527,7 @@
         if (mTabSelectionEditorActions == null) {
             mTabSelectionEditorActions = new ArrayList<>();
             mTabSelectionEditorActions.add(TabSelectionEditorSelectionAction.createAction(mActivity,
-                    ShowMode.IF_ROOM, ButtonType.ICON_AND_TEXT, IconPosition.END,
+                    ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.END,
                     mTabModelSelector.getCurrentModel().isIncognito()));
             mTabSelectionEditorActions.add(TabSelectionEditorCloseAction.createAction(
                     mActivity, ShowMode.MENU_ONLY, ButtonType.ICON_AND_TEXT, IconPosition.START));
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
index 6a7952e..c6696518 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
@@ -159,18 +159,10 @@
      * icon background.
      *
      * @param context {@link Context} used to retrieve color.
-     * @param isIncognito Whether the color is used for incognito mode.
      * @return The {@link ColorInt} for select all icon background.
      */
-    public static @ColorInt int getSelectionActionIconBackgroundColor(
-            Context context, boolean isIncognito) {
-        if (isIncognito) {
-            return context.getColor(
-                    R.color.incognito_tab_selection_editor_selection_action_bg_color);
-        }
-        @ColorInt
-        int colorInt = MaterialColors.getColor(context, R.attr.colorOnSurface, TAG);
-        return colorInt;
+    public static @ColorInt int getSelectionActionIconBackgroundColor(Context context) {
+        return MaterialColors.getColor(context, R.attr.colorOnSurfaceVariant, TAG);
     }
 
     /**
@@ -178,16 +170,10 @@
      * {@link TabSelectionEditorSelectionAction}.
      *
      * @param context {@link Context} used to retrieve color.
-     * @param isIncognito Whether the color is used for incognito mode.
      * @return The {@link ColorInt} for "check" drawable.
      */
-    public static @ColorInt int getSelectionActionIconCheckedDrawableColor(
-            Context context, boolean isIncognito) {
-        if (isIncognito) {
-            return context.getColor(R.color.incognito_tab_selection_editor_selection_action_check);
-        }
-        return MaterialColors.getColor(
-                context, org.chromium.chrome.R.attr.colorOnSurfaceInverse, TAG);
+    public static @ColorInt int getSelectionActionIconCheckedDrawableColor(Context context) {
+        return MaterialColors.getColor(context, org.chromium.chrome.R.attr.colorOnPrimary, TAG);
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
index ef13589..800e9ac 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -1183,6 +1183,38 @@
     @MediumTest
     @EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
+    public void testToolbarMenuItem_SelectAllMenu() {
+        prepareBlankTab(2, false);
+        List<Tab> tabs = getTabsInCurrentTabModel();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            List<TabSelectionEditorAction> actions = new ArrayList<>();
+            actions.add(TabSelectionEditorSelectionAction.createAction(
+                    sActivityTestRule.getActivity(), ShowMode.MENU_ONLY, ButtonType.TEXT,
+                    IconPosition.START, /*isIncognito=*/false));
+            actions.add(TabSelectionEditorCloseAction.createAction(sActivityTestRule.getActivity(),
+                    ShowMode.MENU_ONLY, ButtonType.TEXT, IconPosition.START));
+
+            mTabSelectionEditorController.configureToolbarWithMenuItems(actions, null);
+            mTabSelectionEditorController.show(tabs);
+        });
+        mRobot.resultRobot.verifyTabSelectionEditorIsVisible();
+
+        mRobot.actionRobot.clickToolbarMenuButton();
+        mRobot.resultRobot.verifyToolbarMenuItemState("Select all", /*enabled=*/true)
+                .verifyToolbarMenuItemState("Close tabs", /*enabled=*/false);
+        mRobot.actionRobot.clickToolbarMenuItem("Select all");
+        mRobot.resultRobot.verifyToolbarMenuItemState("Deselect all", /*enabled=*/true)
+                .verifyToolbarMenuItemState("Close tabs", /*enabled=*/true);
+        mRobot.actionRobot.clickToolbarMenuItem("Deselect all");
+        mRobot.resultRobot.verifyToolbarMenuItemState("Select all", /*enabled=*/true);
+        Espresso.pressBack();
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
+    @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
     public void testToolbarActionViewAndMenuItemContentDescription() {
         prepareBlankTab(2, false);
         List<Tab> tabs = getTabsInCurrentTabModel();
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index 2cc3ef0..89ed83da 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -696,10 +696,11 @@
      * @param kind Kind of stream being created.
      * @return The FeedStream created.
      */
-    FeedStream createFeedStream(@StreamKind int kind) {
+    FeedStream createFeedStream(@StreamKind int kind, Stream.StreamsMediator streamsMediator) {
         return new FeedStream(mActivity, mSnackbarManager, mBottomSheetController,
                 mIsPlaceholderShownInitially, mWindowAndroid, mShareSupplier, kind, this,
-                mActionDelegate, mHelpAndFeedbackLauncher, this /* FeedContentFirstLoadWatcher */);
+                mActionDelegate, mHelpAndFeedbackLauncher, this /* FeedContentFirstLoadWatcher */,
+                streamsMediator);
     }
 
     private void setHeaders(List<View> headerViews) {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
index c5942464..bfa6d50 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -85,28 +85,7 @@
     private class FeedSurfaceHeaderSelectedCallback implements OnSectionHeaderSelectedListener {
         @Override
         public void onSectionHeaderSelected(int index) {
-            PropertyListModel<PropertyModel, PropertyKey> headerList =
-                    mSectionHeaderModel.get(SectionHeaderListProperties.SECTION_HEADERS_KEY);
-            mSectionHeaderModel.set(SectionHeaderListProperties.CURRENT_TAB_INDEX_KEY, index);
-
-            // Proactively disable the unread content. Waiting for observers is too slow.
-            headerList.get(index).set(SectionHeaderProperties.UNREAD_CONTENT_KEY, false);
-
-            FeedFeatures.setLastSeenFeedTabId(index);
-
-            Stream newStream = mTabToStreamMap.get(index);
-            if (newStream.supportsOptions()) {
-                headerList.get(index).set(SectionHeaderProperties.OPTIONS_INDICATOR_VISIBILITY_KEY,
-                        ViewVisibility.VISIBLE);
-            }
-            updateLayout(newStream.supportsOptions(), false);
-            if (!mSettingUpStreams) {
-                logSwitchedFeeds(newStream);
-                bindStream(newStream, /*shouldScrollToTop=*/true);
-                if (newStream.getStreamKind() == StreamKind.FOLLOWING) {
-                    FeedFeatures.updateFollowingFeedSeen();
-                }
-            }
+            switchToStream(index);
         }
 
         @Override
@@ -181,6 +160,21 @@
         }
     }
 
+    /**
+     * Internal implementation of Stream.StreamsMediator.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public class StreamsMediatorImpl implements Stream.StreamsMediator {
+        @Override
+        public void switchToStreamKind(@StreamKind int streamKind) {
+            int headerIndex = getTabIdForSection(streamKind);
+            assert headerIndex != -1 : "Invalid header index for streamKind=" + streamKind;
+            if (headerIndex != -1) {
+                FeedSurfaceMediator.this.switchToStream(headerIndex);
+            }
+        }
+    }
+
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public static void setPrefForTest(
             PrefChangeRegistrar prefChangeRegistrar, PrefService prefService) {
@@ -302,6 +296,32 @@
         listLayoutHelper.setSpanCount(spanCount);
     }
 
+    private void switchToStream(int headerIndex) {
+        PropertyListModel<PropertyModel, PropertyKey> headerList =
+                mSectionHeaderModel.get(SectionHeaderListProperties.SECTION_HEADERS_KEY);
+        mSectionHeaderModel.set(SectionHeaderListProperties.CURRENT_TAB_INDEX_KEY, headerIndex);
+
+        // Proactively disable the unread content. Waiting for observers is too slow.
+        headerList.get(headerIndex).set(SectionHeaderProperties.UNREAD_CONTENT_KEY, false);
+
+        FeedFeatures.setLastSeenFeedTabId(headerIndex);
+
+        Stream newStream = mTabToStreamMap.get(headerIndex);
+        if (newStream.supportsOptions()) {
+            headerList.get(headerIndex)
+                    .set(SectionHeaderProperties.OPTIONS_INDICATOR_VISIBILITY_KEY,
+                            ViewVisibility.VISIBLE);
+        }
+        updateLayout(newStream.supportsOptions(), false);
+        if (!mSettingUpStreams) {
+            logSwitchedFeeds(newStream);
+            bindStream(newStream, /*shouldScrollToTop=*/true);
+            if (newStream.getStreamKind() == StreamKind.FOLLOWING) {
+                FeedFeatures.updateFollowingFeedSeen();
+            }
+        }
+    }
+
     /** Clears any dependencies. */
     void destroy() {
         destroyPropertiesForStream();
@@ -431,7 +451,7 @@
         boolean suggestionsVisible = isSuggestionsVisible();
 
         addHeaderAndStream(getInterestFeedHeaderText(suggestionsVisible),
-                mCoordinator.createFeedStream(StreamKind.FOR_YOU));
+                mCoordinator.createFeedStream(StreamKind.FOR_YOU, new StreamsMediatorImpl()));
         setHeaderIndicatorState(suggestionsVisible);
 
         // Build menu after section enabled key is set.
@@ -542,7 +562,7 @@
         if (hasWebFeedTab == shouldHaveWebFeedTab) return;
         if (shouldHaveWebFeedTab) {
             addHeaderAndStream(mContext.getResources().getString(R.string.ntp_following),
-                    mCoordinator.createFeedStream(StreamKind.FOLLOWING));
+                    mCoordinator.createFeedStream(StreamKind.FOLLOWING, new StreamsMediatorImpl()));
             if (FeedFeatures.shouldUseNewIndicator()) {
                 PropertyModel followingHeaderModel =
                         mSectionHeaderModel.get(SectionHeaderListProperties.SECTION_HEADERS_KEY)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
index 36b0ee96..0170ee4e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
@@ -144,9 +144,11 @@
         when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
         when(mFeedSurfaceCoordinator.isActive()).thenReturn(true);
         when(mFeedSurfaceCoordinator.getRecyclerView()).thenReturn(new RecyclerView(mActivity));
-        when(mFeedSurfaceCoordinator.createFeedStream(eq(StreamKind.FOLLOWING)))
+        when(mFeedSurfaceCoordinator.createFeedStream(
+                     eq(StreamKind.FOLLOWING), any(Stream.StreamsMediator.class)))
                 .thenReturn(mFollowingStream);
-        when(mFeedSurfaceCoordinator.createFeedStream(eq(StreamKind.FOR_YOU)))
+        when(mFeedSurfaceCoordinator.createFeedStream(
+                     eq(StreamKind.FOR_YOU), any(Stream.StreamsMediator.class)))
                 .thenReturn(mForYouStream);
         when(mFeedSurfaceCoordinator.getReliabilityLogger()).thenReturn(mReliabilityLogger);
         when(mReliabilityLogger.getLaunchLogger()).thenReturn(mLaunchReliabilityLogger);
@@ -157,9 +159,9 @@
         ObservableSupplierImpl<Boolean> hasUnreadContent = new ObservableSupplierImpl<>();
         hasUnreadContent.set(false);
         when(mForYouStream.hasUnreadContent()).thenReturn(hasUnreadContent);
-        when(mForYouStream.getStreamKind()).thenReturn(1);
+        when(mForYouStream.getStreamKind()).thenReturn(StreamKind.FOR_YOU);
         when(mFollowingStream.hasUnreadContent()).thenReturn(hasUnreadContent);
-        when(mFollowingStream.getStreamKind()).thenReturn(2);
+        when(mFollowingStream.getStreamKind()).thenReturn(StreamKind.FOLLOWING);
 
         FeedSurfaceMediator.setPrefForTest(mPrefChangeRegistrar, mPrefService);
         FeedFeatures.setFakePrefsForTest(mPrefService);
@@ -541,6 +543,13 @@
 
     private OnSectionHeaderSelectedListener getOnSectionHeaderSelectedListener(
             PropertyModel model, PropertyModel forYou, boolean value) {
+        // Notes:
+        // * The returned PropertyModel's will be configured to simulate the Following feed being
+        //   the one selected.
+        // * There's no point in returning PropertyModel's for the Following (or other) headers
+        //   because individual Mediator onSectionHeader* will only affect the acted-on header. At
+        //   runtime, many of them will be called in sequence to keep all headers in a consistent
+        //   state.
         model.get(SectionHeaderListProperties.SECTION_HEADERS_KEY).add(forYou);
         forYou.set(SectionHeaderProperties.UNREAD_CONTENT_KEY, true);
         forYou.set(SectionHeaderProperties.OPTIONS_INDICATOR_VISIBILITY_KEY, ViewVisibility.GONE);
@@ -549,6 +558,8 @@
                 .add(SectionHeaderProperties.createSectionHeader("Following"));
         mFeedSurfaceMediator = createMediator(FeedSurfaceCoordinator.StreamTabId.FOLLOWING, model);
 
+        model.set(SectionHeaderListProperties.CURRENT_TAB_INDEX_KEY, 1);
+
         when(mForYouStream.supportsOptions()).thenReturn(value);
         mFeedSurfaceMediator.setStreamForTesting(
                 FeedSurfaceCoordinator.StreamTabId.FOLLOWING, mForYouStream);
@@ -740,6 +751,22 @@
                 .logSwitchedFeeds(eq(StreamType.FOR_YOU), anyLong());
     }
 
+    @Test
+    public void testStreamsMediatorImpl_switchToStreamKind() {
+        PropertyModel model = SectionHeaderListProperties.create(TOOLBAR_HEIGHT);
+        PropertyModel forYou = SectionHeaderProperties.createSectionHeader("For you");
+        OnSectionHeaderSelectedListener listener =
+                getOnSectionHeaderSelectedListener(model, forYou, true);
+
+        Stream.StreamsMediator streamsMediator = mFeedSurfaceMediator.new StreamsMediatorImpl();
+        streamsMediator.switchToStreamKind(StreamKind.FOR_YOU);
+
+        assertEquals(0, model.get(SectionHeaderListProperties.CURRENT_TAB_INDEX_KEY));
+        assertEquals(false, forYou.get(SectionHeaderProperties.UNREAD_CONTENT_KEY));
+        assertEquals(ViewVisibility.VISIBLE,
+                forYou.get(SectionHeaderProperties.OPTIONS_INDICATOR_VISIBILITY_KEY));
+    }
+
     private FeedSurfaceMediator createMediator() {
         return createMediator(FeedSurfaceCoordinator.StreamTabId.FOR_YOU,
                 SectionHeaderListProperties.create(TOOLBAR_HEIGHT));
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4bfc8e90..b76a49d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5252,8 +5252,8 @@
       "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
       "//chrome/browser/ui/webui/ash/emoji:mojo_bindings",
       "//chrome/browser/ui/webui/ash/launcher_internals:mojo_bindings",
+      "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/manage_mirrorsync:mojo_bindings",
-      "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/vm:mojo_bindings",
       "//chrome/browser/ui/webui/nearby_share:mojom",
       "//chrome/browser/ui/webui/nearby_share/public/mojom",
@@ -8076,7 +8076,7 @@
       "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings_js",
       "//chrome/browser/ui/webui/ash/crostini_installer:mojo_bindings_js",
       "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings_js",
-      "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings_js",
+      "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/vm:mojo_bindings_webui_js",
       "//chrome/browser/ui/webui/settings/ash:mojom_js",
       "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom:mojom_js",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 548f9a518..0688458 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -7400,9 +7400,6 @@
     {"exo-gamepad-vibration", flag_descriptions::kExoGamepadVibrationName,
      flag_descriptions::kExoGamepadVibrationDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kGamepadVibration)},
-    {"exo-lock-notification", flag_descriptions::kExoLockNotificationName,
-     flag_descriptions::kExoLockNotificationDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kExoLockNotification)},
     {"exo-ordinal-motion", flag_descriptions::kExoOrdinalMotionName,
      flag_descriptions::kExoOrdinalMotionDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kExoOrdinalMotion)},
@@ -9196,11 +9193,6 @@
      flag_descriptions::kEnableFakeKeyboardHeuristicDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ui::kEnableFakeKeyboardHeuristic)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-    {"initial-navigation-entry", flag_descriptions::kInitialNavigationEntryName,
-     flag_descriptions::kInitialNavigationEntryDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kInitialNavigationEntry)},
-
 #if !BUILDFLAG(IS_ANDROID)
     {"enable-isolated-sandboxed-iframes",
      flag_descriptions::kIsolatedSandboxedIframesName,
diff --git a/chrome/browser/ash/accessibility/magnification_controller_browsertest.cc b/chrome/browser/ash/accessibility/magnification_controller_browsertest.cc
index c448eac8..94c12c0 100644
--- a/chrome/browser/ash/accessibility/magnification_controller_browsertest.cc
+++ b/chrome/browser/ash/accessibility/magnification_controller_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
+#include "chrome/browser/ash/accessibility/magnifier_animation_waiter.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -60,34 +61,6 @@
   return GetFullscreenMagnifierController()->GetViewportRect();
 }
 
-class MagnifierAnimationWaiter {
- public:
-  explicit MagnifierAnimationWaiter(FullscreenMagnifierController* controller)
-      : controller_(controller) {}
-
-  MagnifierAnimationWaiter(const MagnifierAnimationWaiter&) = delete;
-  MagnifierAnimationWaiter& operator=(const MagnifierAnimationWaiter&) = delete;
-
-  void Wait() {
-    base::RepeatingTimer check_timer;
-    check_timer.Start(FROM_HERE, base::Milliseconds(10), this,
-                      &MagnifierAnimationWaiter::OnTimer);
-    runner_ = new content::MessageLoopRunner;
-    runner_->Run();
-  }
-
- private:
-  void OnTimer() {
-    DCHECK(runner_.get());
-    if (!controller_->IsOnAnimationForTesting()) {
-      runner_->Quit();
-    }
-  }
-
-  FullscreenMagnifierController* controller_;  // not owned
-  scoped_refptr<content::MessageLoopRunner> runner_;
-};
-
 }  // namespace
 
 class FullscreenMagnifierControllerTest : public InProcessBrowserTest {
diff --git a/chrome/browser/ash/accessibility/magnifier_animation_waiter.cc b/chrome/browser/ash/accessibility/magnifier_animation_waiter.cc
new file mode 100644
index 0000000..fd4e57b3
--- /dev/null
+++ b/chrome/browser/ash/accessibility/magnifier_animation_waiter.cc
@@ -0,0 +1,32 @@
+// 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/ash/accessibility/magnifier_animation_waiter.h"
+#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
+#include "base/timer/timer.h"
+
+namespace ash {
+
+MagnifierAnimationWaiter::MagnifierAnimationWaiter(
+    FullscreenMagnifierController* controller)
+    : controller_(controller) {}
+
+MagnifierAnimationWaiter::~MagnifierAnimationWaiter() = default;
+
+void MagnifierAnimationWaiter::Wait() {
+  base::RepeatingTimer check_timer;
+  check_timer.Start(FROM_HERE, base::Milliseconds(10), this,
+                    &MagnifierAnimationWaiter::OnTimer);
+  runner_ = new content::MessageLoopRunner;
+  runner_->Run();
+}
+
+void MagnifierAnimationWaiter::OnTimer() {
+  DCHECK(runner_.get());
+  if (!controller_->IsOnAnimationForTesting()) {
+    runner_->Quit();
+  }
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/accessibility/magnifier_animation_waiter.h b/chrome/browser/ash/accessibility/magnifier_animation_waiter.h
new file mode 100644
index 0000000..4e12177
--- /dev/null
+++ b/chrome/browser/ash/accessibility/magnifier_animation_waiter.h
@@ -0,0 +1,38 @@
+// 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_ASH_ACCESSIBILITY_MAGNIFIER_ANIMATION_WAITER_H_
+#define CHROME_BROWSER_ASH_ACCESSIBILITY_MAGNIFIER_ANIMATION_WAITER_H_
+
+#include "content/public/test/test_utils.h"
+
+namespace ash {
+
+class FullscreenMagnifierController;
+
+// FullscreenMagnifierController moves the magnifier window with animation
+// when the magnifier is set to be enabled. This waiter class lets a consumer
+// wait until the animation completes, i.e. after a mouse move.
+class MagnifierAnimationWaiter {
+ public:
+  explicit MagnifierAnimationWaiter(FullscreenMagnifierController* controller);
+
+  MagnifierAnimationWaiter(const MagnifierAnimationWaiter&) = delete;
+  MagnifierAnimationWaiter& operator=(const MagnifierAnimationWaiter&) = delete;
+
+  ~MagnifierAnimationWaiter();
+
+  // Wait until the Fullscreen magnifier finishes animating.
+  void Wait();
+
+ private:
+  void OnTimer();
+
+  FullscreenMagnifierController* controller_;  // not owned
+  scoped_refptr<content::MessageLoopRunner> runner_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_ACCESSIBILITY_MAGNIFIER_ANIMATION_WAITER_H_
diff --git a/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc b/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc
index 915810f..79ede7e0 100644
--- a/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc
+++ b/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc
@@ -23,6 +23,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
+#include "chrome/browser/ash/accessibility/magnifier_animation_waiter.h"
 #include "chrome/browser/ash/accessibility/speech_monitor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -200,35 +201,6 @@
   base::WeakPtrFactory<SelectToSpeakTest> weak_ptr_factory_{this};
 };
 
-// TODO(crbug.com/1375293): Extract MagnifierAnimationWaiter to helper.
-class MagnifierAnimationWaiter {
- public:
-  explicit MagnifierAnimationWaiter(FullscreenMagnifierController* controller)
-      : controller_(controller) {}
-
-  MagnifierAnimationWaiter(const MagnifierAnimationWaiter&) = delete;
-  MagnifierAnimationWaiter& operator=(const MagnifierAnimationWaiter&) = delete;
-
-  void Wait() {
-    base::RepeatingTimer check_timer;
-    check_timer.Start(FROM_HERE, base::Milliseconds(10), this,
-                      &MagnifierAnimationWaiter::OnTimer);
-    runner_ = new content::MessageLoopRunner;
-    runner_->Run();
-  }
-
- private:
-  void OnTimer() {
-    DCHECK(runner_.get());
-    if (!controller_->IsOnAnimationForTesting()) {
-      runner_->Quit();
-    }
-  }
-
-  FullscreenMagnifierController* controller_;  // not owned
-  scoped_refptr<content::MessageLoopRunner> runner_;
-};
-
 class SelectToSpeakTestWithVoiceSwitching : public SelectToSpeakTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/ash/arc/session/arc_disk_space_monitor.cc b/chrome/browser/ash/arc/session/arc_disk_space_monitor.cc
index f7b24ab..955211a 100644
--- a/chrome/browser/ash/arc/session/arc_disk_space_monitor.cc
+++ b/chrome/browser/ash/arc/session/arc_disk_space_monitor.cc
@@ -52,8 +52,9 @@
 
 void ArcDiskSpaceMonitor::CheckDiskSpace() {
   ash::SpacedClient::Get()->GetFreeDiskSpace(
-      "/home", base::BindOnce(&ArcDiskSpaceMonitor::OnGetFreeDiskSpace,
-                              weak_ptr_factory_.GetWeakPtr()));
+      "/home/chronos/user",
+      base::BindOnce(&ArcDiskSpaceMonitor::OnGetFreeDiskSpace,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcDiskSpaceMonitor::OnGetFreeDiskSpace(absl::optional<int64_t> reply) {
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc b/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc
index 9601c60..5df7c119 100644
--- a/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc
+++ b/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/system/sys_info.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/borealis/borealis_context.h"
 #include "chrome/browser/ash/borealis/borealis_context_manager.h"
@@ -24,6 +23,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 
 namespace borealis {
 namespace {
@@ -69,12 +69,9 @@
 constexpr int64_t kTargetBufferUpperBound = kTargetBufferBytes * 1.1;
 
 void BorealisDiskManagerImpl::FreeSpaceProvider::Get(
-    base::OnceCallback<void(int64_t)> callback) {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
-                     base::FilePath(crostini::kHomeDirectory)),
-      std::move(callback));
+    base::OnceCallback<void(absl::optional<int64_t>)> callback) {
+  ash::SpacedClient::Get()->GetFreeDiskSpace(crostini::kHomeDirectory,
+                                             std::move(callback));
 }
 
 struct BorealisDiskManagerImpl::BorealisDiskInfo {
@@ -141,15 +138,15 @@
   }
 
  private:
-  void HandleFreeSpaceResult(int64_t free_space) {
-    if (free_space < 0) {
+  void HandleFreeSpaceResult(absl::optional<int64_t> free_space) {
+    if (!free_space.has_value() || free_space.value() < 0) {
       Fail(Described<BorealisGetDiskInfoResult>(
           BorealisGetDiskInfoResult::kFailedGettingExpandableSpace,
           "failed to get the amount of free disk space on the host"));
       return;
     }
     disk_info_->expandable_space =
-        std::max(int64_t(free_space - kDiskHeadroomBytes), int64_t(0));
+        std::max(int64_t(free_space.value() - kDiskHeadroomBytes), int64_t(0));
     vm_tools::concierge::ListVmDisksRequest request;
     request.set_cryptohome_id(
         ash::ProfileHelper::GetUserIdHashFromProfile(context_->profile()));
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_impl.h b/chrome/browser/ash/borealis/borealis_disk_manager_impl.h
index 26ad281..71099e2b 100644
--- a/chrome/browser/ash/borealis/borealis_disk_manager_impl.h
+++ b/chrome/browser/ash/borealis/borealis_disk_manager_impl.h
@@ -25,7 +25,8 @@
    public:
     FreeSpaceProvider() = default;
     virtual ~FreeSpaceProvider() = default;
-    virtual void Get(base::OnceCallback<void(int64_t)> callback);
+    virtual void Get(
+        base::OnceCallback<void(absl::optional<int64_t>)> callback);
   };
 
   explicit BorealisDiskManagerImpl(const BorealisContext* context);
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc b/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc
index e2945d5..a60daec0 100644
--- a/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc
+++ b/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc
@@ -38,10 +38,13 @@
 
 constexpr uint64_t kGiB = 1024 * 1024 * 1024;
 
+using BorealisGetDiskSpaceInfoCallback =
+    base::OnceCallback<void(absl::optional<int64_t>)>;
+
 class FreeSpaceProviderMock
     : public BorealisDiskManagerImpl::FreeSpaceProvider {
  public:
-  MOCK_METHOD(void, Get, (base::OnceCallback<void(int64_t)>), ());
+  MOCK_METHOD(void, Get, (BorealisGetDiskSpaceInfoCallback), ());
 };
 
 using DiskInfoCallbackFactory = StrictCallbackFactory<void(
@@ -172,7 +175,7 @@
 
 TEST_F(BorealisDiskManagerTest, GetDiskInfoFailsOnFreeSpaceProviderError) {
   EXPECT_CALL(*free_space_provider_, Get(_))
-      .WillOnce(testing::Invoke([](base::OnceCallback<void(int64_t)> callback) {
+      .WillOnce(testing::Invoke([](BorealisGetDiskSpaceInfoCallback callback) {
         std::move(callback).Run(-1);
       }));
 
@@ -195,7 +198,7 @@
 TEST_F(BorealisDiskManagerTest, GetDiskInfoFailsOnNoResponseFromConcierge) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             // Concierge will return an empty ListVmDisksResponse.
             FakeConciergeClient()->set_list_vm_disks_response(
                 absl::optional<vm_tools::concierge::ListVmDisksResponse>());
@@ -222,7 +225,7 @@
        GetDiskInfoFailsOnUnsuccessfulResponseFromConcierge) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/8 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -247,7 +250,7 @@
 TEST_F(BorealisDiskManagerTest, GetDiskInfoFailsOnVmMismatch) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/8 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -272,7 +275,7 @@
 TEST_F(BorealisDiskManagerTest, GetDiskInfoSucceedsAndReturnsResponse) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -303,7 +306,7 @@
 TEST_F(BorealisDiskManagerTest, GetDiskInfoReservesExpandableSpaceForBuffer) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -335,7 +338,7 @@
        GetDiskInfoReturns0AvailableSpaceForSparseDisks) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -363,7 +366,7 @@
 TEST_F(BorealisDiskManagerTest, GetDiskInfoFailsOnConcurrentAttempt) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -402,12 +405,11 @@
   testing::InSequence sequence;
 
   EXPECT_CALL(*free_space_provider_, Get(_))
-      .WillOnce(
-          testing::Invoke([this, response = response](
-                              base::OnceCallback<void(int64_t)> callback) {
-            FakeConciergeClient()->set_list_vm_disks_response(response);
-            std::move(callback).Run(2 * kGiB);
-          }));
+      .WillOnce(testing::Invoke([this, response = response](
+                                    BorealisGetDiskSpaceInfoCallback callback) {
+        FakeConciergeClient()->set_list_vm_disks_response(response);
+        std::move(callback).Run(2 * kGiB);
+      }));
 
   DiskInfoCallbackFactory first_callback_factory;
   EXPECT_CALL(first_callback_factory, Call(_))
@@ -420,12 +422,11 @@
   run_loop()->RunUntilIdle();
 
   EXPECT_CALL(*free_space_provider_, Get(_))
-      .WillOnce(
-          testing::Invoke([this, response = response](
-                              base::OnceCallback<void(int64_t)> callback) {
-            FakeConciergeClient()->set_list_vm_disks_response(response);
-            std::move(callback).Run(2 * kGiB);
-          }));
+      .WillOnce(testing::Invoke([this, response = response](
+                                    BorealisGetDiskSpaceInfoCallback callback) {
+        FakeConciergeClient()->set_list_vm_disks_response(response);
+        std::move(callback).Run(2 * kGiB);
+      }));
 
   DiskInfoCallbackFactory second_callback_factory;
   EXPECT_CALL(second_callback_factory, Call(_))
@@ -496,7 +497,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsIfBuildDiskInfoFails) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -527,7 +528,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsIfDiskTypeNotRaw) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -556,7 +557,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsIfRequestTooHigh) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -588,7 +589,7 @@
        RequestDeltaFailsIfRequestWouldNotLeaveEnoughSpace) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -617,7 +618,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsIfDiskIsBelowMinimum) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/8 * kGiB, /*size=*/7 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -651,7 +652,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/7 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -666,7 +667,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/6 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -697,7 +698,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsOnNoResizeDiskResponse) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -724,7 +725,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsOnFailedResizeDiskResponse) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -753,7 +754,7 @@
 TEST_F(BorealisDiskManagerTest, RequestDeltaFailsOnDelayedConciergeFailure) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -794,7 +795,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -808,7 +809,7 @@
   FakeConciergeClient()->set_resize_disk_image_response(disk_response);
 
   EXPECT_CALL(*free_space_provider_, Get(_))
-      .WillOnce(testing::Invoke([](base::OnceCallback<void(int64_t)> callback) {
+      .WillOnce(testing::Invoke([](BorealisGetDiskSpaceInfoCallback callback) {
         std::move(callback).Run(-1 * kGiB);
       }));
 
@@ -834,8 +835,8 @@
   testing::InSequence sequence;
 
   EXPECT_CALL(*free_space_provider_, Get(_))
-      .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+      .WillOnce(testing::Invoke(
+          [this](base::OnceCallback<void(absl::optional<int64_t>)> callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -850,7 +851,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/21 * kGiB,
                 /*available_space=*/5 * kGiB);
@@ -881,7 +882,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -896,7 +897,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/21 * kGiB,
                 /*available_space=*/5 * kGiB);
@@ -927,7 +928,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -942,7 +943,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/22 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -972,7 +973,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -987,7 +988,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/19 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1017,7 +1018,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/100 * kGiB);
@@ -1033,7 +1034,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             // Note: available_space goes from 100 GB to 3 GB, when requesting
             // for more space. In a sparse disk, the available space on the
             // disk is more like the expandable space on the disk, so it makes
@@ -1065,7 +1066,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/100 * kGiB);
@@ -1081,7 +1082,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             // Note: available_space goes from 100 GB to 2 GB, when releasing
             // space. In a sparse disk, the available space on the disk is more
             // like the expandable space on the disk, so it makes sense that
@@ -1113,7 +1114,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -1128,7 +1129,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/23 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -1161,7 +1162,7 @@
   // fulfilled.
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/22 * kGiB,
                 /*available_space=*/5 * kGiB);
@@ -1171,7 +1172,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1207,7 +1208,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1222,7 +1223,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/22 * kGiB,
                 /*available_space=*/5 * kGiB);
@@ -1240,7 +1241,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/22 * kGiB,
                 /*available_space=*/5 * kGiB);
@@ -1255,7 +1256,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/21 * kGiB,
                 /*available_space=*/4 * kGiB);
@@ -1278,7 +1279,7 @@
 TEST_F(BorealisDiskManagerTest, SyncDiskSizeFailsIfGetDiskInfoFails) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1306,7 +1307,7 @@
 TEST_F(BorealisDiskManagerTest, SyncDiskSizeSucceedsIfDiskNotFixedSize) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1334,7 +1335,7 @@
 TEST_F(BorealisDiskManagerTest, SyncDiskSizeSucceedsIfDiskCantExpand) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -1367,7 +1368,7 @@
 TEST_F(BorealisDiskManagerTest, SyncDiskSizeSucceedsIfDiskDoesntNeedToExpand) {
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/20 * kGiB,
                 /*available_space=*/2 * kGiB);
@@ -1399,7 +1400,7 @@
   EXPECT_CALL(*free_space_provider_, Get(_))
       .Times(2)
       .WillRepeatedly(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/6 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1432,7 +1433,7 @@
   EXPECT_CALL(*free_space_provider_, Get(_))
       .Times(2)
       .WillRepeatedly(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/5 * kGiB,
                 /*available_space=*/3 * kGiB);
@@ -1468,7 +1469,7 @@
   EXPECT_CALL(*free_space_provider_, Get(_))
       .Times(2)
       .WillRepeatedly(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/7 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -1483,7 +1484,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/7.5 * kGiB,
                 /*available_space=*/1.5 * kGiB);
@@ -1517,7 +1518,7 @@
   EXPECT_CALL(*free_space_provider_, Get(_))
       .Times(2)
       .WillRepeatedly(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/7 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -1532,7 +1533,7 @@
 
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/8 * kGiB,
                 /*available_space=*/2 * kGiB);
@@ -1563,7 +1564,7 @@
   // fulfilled.
   EXPECT_CALL(*free_space_provider_, Get(_))
       .WillOnce(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/8 * kGiB,
                 /*available_space=*/2 * kGiB);
@@ -1574,7 +1575,7 @@
   EXPECT_CALL(*free_space_provider_, Get(_))
       .Times(2)
       .WillRepeatedly(
-          testing::Invoke([this](base::OnceCallback<void(int64_t)> callback) {
+          testing::Invoke([this](BorealisGetDiskSpaceInfoCallback callback) {
             auto response = BuildValidListVmDisksResponse(
                 /*min_size=*/6 * kGiB, /*size=*/7 * kGiB,
                 /*available_space=*/1 * kGiB);
@@ -1616,7 +1617,7 @@
 
 TEST_F(BorealisDiskManagerTest, RequestsRecordedOnDestruction) {
   EXPECT_CALL(*free_space_provider_, Get(_))
-      .WillOnce(testing::Invoke([](base::OnceCallback<void(int64_t)> callback) {
+      .WillOnce(testing::Invoke([](BorealisGetDiskSpaceInfoCallback callback) {
         std::move(callback).Run(-1);
       }));
   DiskInfoCallbackFactory callback_factory;
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 0e36002..507f221b 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -261,7 +261,7 @@
     "//chrome/browser/image_decoder:image_decoder",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/ash/system_web_apps",
-    "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings",
+    "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
     "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
     "//chrome/browser/web_applications",
     "//chrome/common",
diff --git a/chrome/browser/ash/crosapi/parent_access_ash.cc b/chrome/browser/ash/crosapi/parent_access_ash.cc
index a0eac07..7818985 100644
--- a/chrome/browser/ash/crosapi/parent_access_ash.cc
+++ b/chrome/browser/ash/crosapi/parent_access_ash.cc
@@ -5,7 +5,7 @@
 #include <vector>
 
 #include "chrome/browser/ash/crosapi/parent_access_ash.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
@@ -13,28 +13,28 @@
 namespace crosapi {
 
 crosapi::mojom::ParentAccessResultPtr DialogResultToParentAccessResult(
-    std::unique_ptr<chromeos::ParentAccessDialog::Result> result) {
+    std::unique_ptr<ash::ParentAccessDialog::Result> result) {
   crosapi::mojom::ParentAccessResultPtr parent_access_result;
 
   switch (result->status) {
-    case chromeos::ParentAccessDialog::Result::Status::kApproved:
+    case ash::ParentAccessDialog::Result::Status::kApproved:
       parent_access_result = mojom::ParentAccessResult::NewApproved(
           crosapi::mojom::ParentAccessApprovedResult::New(
               result->parent_access_token,
               result->parent_access_token_expire_timestamp));
       break;
 
-    case chromeos::ParentAccessDialog::Result::Status::kDeclined:
+    case ash::ParentAccessDialog::Result::Status::kDeclined:
       parent_access_result = mojom::ParentAccessResult::NewDeclined(
           crosapi::mojom::ParentAccessDeclinedResult::New());
       break;
 
-    case chromeos::ParentAccessDialog::Result::Status::kCancelled:
+    case ash::ParentAccessDialog::Result::Status::kCancelled:
       parent_access_result = mojom::ParentAccessResult::NewCancelled(
           crosapi::mojom::ParentAccessCancelledResult::New());
       break;
 
-    case chromeos::ParentAccessDialog::Result::Status::kError:
+    case ash::ParentAccessDialog::Result::Status::kError:
       parent_access_result = mojom::ParentAccessResult::NewError(
           crosapi::mojom::ParentAccessErrorResult::New(
               crosapi::mojom::ParentAccessErrorResult::Type::kUnknown));
@@ -45,22 +45,22 @@
 }
 
 crosapi::mojom::ParentAccessResultPtr ShowErrorToParentAccessResultError(
-    chromeos::ParentAccessDialogProvider::ShowError show_dialog_result) {
+    ash::ParentAccessDialogProvider::ShowError show_dialog_result) {
   crosapi::mojom::ParentAccessResultPtr parent_access_result;
 
   // This result indicates basic errors that can occur synchronously.
   switch (show_dialog_result) {
-    case chromeos::ParentAccessDialogProvider::ShowError::kDialogAlreadyVisible:
+    case ash::ParentAccessDialogProvider::ShowError::kDialogAlreadyVisible:
       parent_access_result = mojom::ParentAccessResult::NewError(
           crosapi::mojom::ParentAccessErrorResult::New(
               crosapi::mojom::ParentAccessErrorResult::Type::kAlreadyVisible));
       break;
-    case chromeos::ParentAccessDialogProvider::ShowError::kNotAChildUser:
+    case ash::ParentAccessDialogProvider::ShowError::kNotAChildUser:
       parent_access_result = mojom::ParentAccessResult::NewError(
           crosapi::mojom::ParentAccessErrorResult::New(
               crosapi::mojom::ParentAccessErrorResult::Type::kNotAChildUser));
       break;
-    case chromeos::ParentAccessDialogProvider::ShowError::kNone:
+    case ash::ParentAccessDialogProvider::ShowError::kNone:
       // The dialog is showing successfully. Wait until we get the result from
       // the dialog before running the crosapi callback. This will be handled
       // in OnParentAccessDialogClosed.
@@ -99,9 +99,9 @@
   ShowParentAccessDialog(std::move(params), std::move(callback));
 }
 
-chromeos::ParentAccessDialogProvider* ParentAccessAsh::GetDialogProvider() {
+ash::ParentAccessDialogProvider* ParentAccessAsh::GetDialogProvider() {
   if (!dialog_provider_)
-    dialog_provider_ = std::make_unique<chromeos::ParentAccessDialogProvider>();
+    dialog_provider_ = std::make_unique<ash::ParentAccessDialogProvider>();
 
   return dialog_provider_.get();
 }
@@ -109,7 +109,7 @@
 void ParentAccessAsh::ShowParentAccessDialog(
     parent_access_ui::mojom::ParentAccessParamsPtr params,
     ParentAccessAsh::ParentAccessCallback callback) {
-  chromeos::ParentAccessDialogProvider::ShowError show_error =
+  ash::ParentAccessDialogProvider::ShowError show_error =
       GetDialogProvider()->Show(
           std::move(params),
           base::BindOnce(&ParentAccessAsh::OnParentAccessDialogClosed,
@@ -130,14 +130,14 @@
   }
 }
 
-chromeos::ParentAccessDialogProvider* ParentAccessAsh::SetDialogProviderForTest(
-    std::unique_ptr<chromeos::ParentAccessDialogProvider> provider) {
+ash::ParentAccessDialogProvider* ParentAccessAsh::SetDialogProviderForTest(
+    std::unique_ptr<ash::ParentAccessDialogProvider> provider) {
   dialog_provider_ = std::move(provider);
   return dialog_provider_.get();
 }
 
 void ParentAccessAsh::OnParentAccessDialogClosed(
-    std::unique_ptr<chromeos::ParentAccessDialog::Result> result) {
+    std::unique_ptr<ash::ParentAccessDialog::Result> result) {
   if (callback_) {
     std::move(callback_).Run(
         DialogResultToParentAccessResult(std::move(result)));
diff --git a/chrome/browser/ash/crosapi/parent_access_ash.h b/chrome/browser/ash/crosapi/parent_access_ash.h
index c5e57d16..66e9546 100644
--- a/chrome/browser/ash/crosapi/parent_access_ash.h
+++ b/chrome/browser/ash/crosapi/parent_access_ash.h
@@ -9,8 +9,8 @@
 #include <string>
 
 #include "base/callback_forward.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chromeos/crosapi/mojom/parent_access.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -45,8 +45,8 @@
   using ParentAccessCallback =
       base::OnceCallback<void(crosapi::mojom::ParentAccessResultPtr)>;
 
-  chromeos::ParentAccessDialogProvider* SetDialogProviderForTest(
-      std::unique_ptr<chromeos::ParentAccessDialogProvider> provider);
+  ash::ParentAccessDialogProvider* SetDialogProviderForTest(
+      std::unique_ptr<ash::ParentAccessDialogProvider> provider);
 
  private:
   // Shows the dialog with the specified parameters.
@@ -60,13 +60,13 @@
   // callback:  The crosapi callback to call with the result.
   // result: The result returned from the dialog.
   void OnParentAccessDialogClosed(
-      std::unique_ptr<chromeos::ParentAccessDialog::Result> result);
+      std::unique_ptr<ash::ParentAccessDialog::Result> result);
 
   // The returned object is lazy initialized.
-  chromeos::ParentAccessDialogProvider* GetDialogProvider();
+  ash::ParentAccessDialogProvider* GetDialogProvider();
 
   mojo::ReceiverSet<mojom::ParentAccess> receivers_;
-  std::unique_ptr<chromeos::ParentAccessDialogProvider> dialog_provider_;
+  std::unique_ptr<ash::ParentAccessDialogProvider> dialog_provider_;
   ParentAccessCallback callback_;
 };
 
diff --git a/chrome/browser/ash/crosapi/parent_access_ash_unittest.cc b/chrome/browser/ash/crosapi/parent_access_ash_unittest.cc
index d2e4954..83ea2601 100644
--- a/chrome/browser/ash/crosapi/parent_access_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/parent_access_ash_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -21,18 +21,17 @@
 #include "ui/gfx/image/image_unittest_util.h"
 #include "url/gurl.h"
 
-class FakeParentAccessDialogProvider
-    : public chromeos::ParentAccessDialogProvider {
+class FakeParentAccessDialogProvider : public ash::ParentAccessDialogProvider {
  public:
   ParentAccessDialogProvider::ShowError Show(
       parent_access_ui::mojom::ParentAccessParamsPtr params,
-      chromeos::ParentAccessDialog::Callback callback) override {
+      ash::ParentAccessDialog::Callback callback) override {
     callback_ = std::move(callback);
     last_params_received_ = std::move(params);
     return next_show_error_;
   }
 
-  void SetNextShowError(chromeos::ParentAccessDialogProvider::ShowError error) {
+  void SetNextShowError(ash::ParentAccessDialogProvider::ShowError error) {
     next_show_error_ = error;
   }
 
@@ -41,14 +40,14 @@
   }
 
   void TriggerCallbackWithResult(
-      std::unique_ptr<chromeos::ParentAccessDialog::Result> result) {
+      std::unique_ptr<ash::ParentAccessDialog::Result> result) {
     std::move(callback_).Run(std::move(result));
   }
 
  private:
-  chromeos::ParentAccessDialog::Callback callback_;
+  ash::ParentAccessDialog::Callback callback_;
   parent_access_ui::mojom::ParentAccessParamsPtr last_params_received_;
-  chromeos::ParentAccessDialogProvider::ShowError next_show_error_;
+  ash::ParentAccessDialogProvider::ShowError next_show_error_;
 };
 
 namespace {
@@ -132,9 +131,8 @@
           },
           run_loop.QuitClosure()));
 
-  auto dialog_result = std::make_unique<chromeos::ParentAccessDialog::Result>();
-  dialog_result->status =
-      chromeos::ParentAccessDialog::Result::Status::kApproved;
+  auto dialog_result = std::make_unique<ash::ParentAccessDialog::Result>();
+  dialog_result->status = ash::ParentAccessDialog::Result::Status::kApproved;
   dialog_result->parent_access_token = "ABC123";
   dialog_result->parent_access_token_expire_timestamp =
       base::Time::FromDoubleT(123456UL);
@@ -157,9 +155,8 @@
           },
           run_loop.QuitClosure()));
 
-  auto dialog_result = std::make_unique<chromeos::ParentAccessDialog::Result>();
-  dialog_result->status =
-      chromeos::ParentAccessDialog::Result::Status::kDeclined;
+  auto dialog_result = std::make_unique<ash::ParentAccessDialog::Result>();
+  dialog_result->status = ash::ParentAccessDialog::Result::Status::kDeclined;
   dialog_provider_->TriggerCallbackWithResult(std::move(dialog_result));
   run_loop.Run();
 }
@@ -181,8 +178,8 @@
           },
           run_loop.QuitClosure()));
 
-  auto dialog_result = std::make_unique<chromeos::ParentAccessDialog::Result>();
-  dialog_result->status = chromeos::ParentAccessDialog::Result::Status::kError;
+  auto dialog_result = std::make_unique<ash::ParentAccessDialog::Result>();
+  dialog_result->status = ash::ParentAccessDialog::Result::Status::kError;
   dialog_provider_->TriggerCallbackWithResult(std::move(dialog_result));
   run_loop.Run();
 }
@@ -202,7 +199,7 @@
           successful_show_run_loop.QuitClosure()));
 
   dialog_provider_->SetNextShowError(
-      chromeos::ParentAccessDialogProvider::ShowError::kDialogAlreadyVisible);
+      ash::ParentAccessDialogProvider::ShowError::kDialogAlreadyVisible);
 
   // Show dialog again, should be blocked because it is already visible.
   base::RunLoop already_visible_run_loop;
@@ -224,9 +221,8 @@
   // The second run loop completes.
   already_visible_run_loop.Run();
 
-  auto dialog_result = std::make_unique<chromeos::ParentAccessDialog::Result>();
-  dialog_result->status =
-      chromeos::ParentAccessDialog::Result::Status::kApproved;
+  auto dialog_result = std::make_unique<ash::ParentAccessDialog::Result>();
+  dialog_result->status = ash::ParentAccessDialog::Result::Status::kApproved;
   dialog_provider_->TriggerCallbackWithResult(std::move(dialog_result));
 
   // The original run loop completes.
@@ -236,7 +232,7 @@
 // Makes sure regular users can't request parent access.
 TEST_F(ParentAccessAshTest, GetWebsiteParentApproval_NotAChildUser) {
   dialog_provider_->SetNextShowError(
-      chromeos::ParentAccessDialogProvider::ShowError::kNotAChildUser);
+      ash::ParentAccessDialogProvider::ShowError::kNotAChildUser);
 
   base::RunLoop run_loop;
   parent_access_ash_->GetWebsiteParentApproval(
diff --git a/chrome/browser/ash/crosapi/test/ash_crosapi_tests_main.cc b/chrome/browser/ash/crosapi/test/ash_crosapi_tests_main.cc
index 15509b1..0943ba35 100644
--- a/chrome/browser/ash/crosapi/test/ash_crosapi_tests_main.cc
+++ b/chrome/browser/ash/crosapi/test/ash_crosapi_tests_main.cc
@@ -4,31 +4,73 @@
 
 #include <memory>
 
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/task/single_thread_task_executor.h"
-#include "base/test/launcher/test_launcher.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
 #include "base/threading/thread.h"
 #include "chrome/browser/ash/crosapi/test/ash_crosapi_tests_env.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 
+namespace {
+
+// Test Suite used for Crosapi Test.
+// It initializes Mojo and AshCrosapiTestEnv.
+class CrosapiTestSuite : public base::TestSuite {
+ public:
+  CrosapiTestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {}
+
+  CrosapiTestSuite(const CrosapiTestSuite&) = delete;
+  CrosapiTestSuite& operator=(const CrosapiTestSuite&) = delete;
+
+  ~CrosapiTestSuite() override = default;
+
+ protected:
+  void Initialize() override {
+    base::TestSuite::Initialize();
+
+    // Starts a new IO thread to run IPC tasks.
+    io_thread_.StartWithOptions(
+        base::Thread::Options(base::MessagePumpType::IO, 0));
+    mojo::core::Init();
+    ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
+        io_thread_.task_runner(),
+        mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+
+    executor_ = std::make_unique<base::SingleThreadTaskExecutor>(
+        base::MessagePumpType::IO);
+
+    // Construct AshCrosapiTestEnv.
+    env_ = std::make_unique<crosapi::AshCrosapiTestEnv>();
+  }
+
+  void Shutdown() override {
+    env_.reset();
+    executor_.reset();
+    ipc_support_.reset();
+    io_thread_.Stop();
+    base::TestSuite::Shutdown();
+  }
+
+ private:
+  base::Thread io_thread_{"MojoThread"};
+  std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support_;
+
+  std::unique_ptr<base::SingleThreadTaskExecutor> executor_;
+  std::unique_ptr<crosapi::AshCrosapiTestEnv> env_;
+};
+
+}  // namespace
+
 int main(int argc, char** argv) {
   base::CommandLine::Init(argc, argv);
 
-  // Starts a new IO thread to run IPC tasks.
-  base::Thread io_thread("MojoThread");
-  io_thread.StartWithOptions(
-      base::Thread::Options(base::MessagePumpType::IO, 0));
-  mojo::core::Init();
-  mojo::core::ScopedIPCSupport ipc_support(
-      io_thread.task_runner(),
-      mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO);
-
-  // Sets up crosapi test environment.
-  auto env = std::make_unique<crosapi::AshCrosapiTestEnv>();
-
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
+  CrosapiTestSuite test_suite(argc, argv);
+  // Run test serially.
+  // TODO(elkurin): Support parallel testing for better performance.
+  return base::LaunchUnitTestsSerially(
+      argc, argv,
+      BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
 }
diff --git a/chrome/browser/ash/crostini/crostini_disk.cc b/chrome/browser/ash/crostini/crostini_disk.cc
index c732d82f..fe43eee 100644
--- a/chrome/browser/ash/crostini/crostini_disk.cc
+++ b/chrome/browser/ash/crostini/crostini_disk.cc
@@ -11,7 +11,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/system/sys_info.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/crostini/crostini_features.h"
 #include "chrome/browser/ash/crostini/crostini_manager.h"
@@ -19,6 +18,7 @@
 #include "chrome/browser/ash/crostini/crostini_types.mojom.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "ui/base/text/bytes_formatting.h"
 
 using DiskImageStatus = vm_tools::concierge::DiskImageStatus;
@@ -66,10 +66,8 @@
     return;
   }
   if (full_info) {
-    base::ThreadPool::PostTaskAndReplyWithResult(
-        FROM_HERE, {base::MayBlock()},
-        base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
-                       base::FilePath(crostini::kHomeDirectory)),
+    ash::SpacedClient::Get()->GetFreeDiskSpace(
+        crostini::kHomeDirectory,
         base::BindOnce(&OnAmountOfFreeDiskSpace, std::move(callback), profile,
                        std::move(vm_name)));
   } else {
@@ -88,8 +86,8 @@
 void OnAmountOfFreeDiskSpace(OnceDiskInfoCallback callback,
                              Profile* profile,
                              std::string vm_name,
-                             int64_t free_space) {
-  if (free_space == 0) {
+                             absl::optional<int64_t> free_space) {
+  if (!free_space.has_value() || free_space.value() <= 0) {
     LOG(ERROR) << "Failed to get amount of free disk space";
     std::move(callback).Run(nullptr);
   } else {
@@ -101,7 +99,7 @@
     CrostiniManager::GetForProfile(profile)->RestartCrostiniWithOptions(
         std::move(container_id), std::move(options),
         base::BindOnce(&OnCrostiniSufficientlyRunning, std::move(callback),
-                       profile, std::move(vm_name), free_space));
+                       profile, std::move(vm_name), free_space.value()));
   }
 }
 
diff --git a/chrome/browser/ash/crostini/crostini_disk.h b/chrome/browser/ash/crostini/crostini_disk.h
index 6dd1e7f..c54de4f 100644
--- a/chrome/browser/ash/crostini/crostini_disk.h
+++ b/chrome/browser/ash/crostini/crostini_disk.h
@@ -66,7 +66,7 @@
 void OnAmountOfFreeDiskSpace(OnceDiskInfoCallback callback,
                              Profile* profile,
                              std::string vm_name,
-                             int64_t free_space);
+                             absl::optional<int64_t> free_space);
 
 // Combined callback for EnsureConciergeRunning or EnsureVmRunning which passes
 // off to the next step in the chain. For getting full disk info, the VM must be
diff --git a/chrome/browser/ash/crostini/crostini_installer.cc b/chrome/browser/ash/crostini/crostini_installer.cc
index 95cca8b..a8245fa 100644
--- a/chrome/browser/ash/crostini/crostini_installer.cc
+++ b/chrome/browser/ash/crostini/crostini_installer.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 #include "chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_dialog.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
@@ -227,12 +228,14 @@
   container_download_percent_ = 0;
   UpdateState(State::INSTALLING);
 
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
-                     base::FilePath(crostini::kHomeDirectory)),
-      base::BindOnce(&CrostiniInstaller::OnAvailableDiskSpace,
-                     weak_ptr_factory_.GetWeakPtr()));
+  // The spaced D-Bus client needs to be called from the ui thread
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ash::SpacedClient::GetFreeDiskSpace,
+                     base::Unretained(ash::SpacedClient::Get()),
+                     crostini::kHomeDirectory,
+                     base::BindOnce(&CrostiniInstaller::OnAvailableDiskSpace,
+                                    weak_ptr_factory_.GetWeakPtr())));
 
   // Reset mic permissions, we don't want it to persist across
   // re-installation.
@@ -595,7 +598,7 @@
   }
 }
 
-void CrostiniInstaller::OnAvailableDiskSpace(int64_t bytes) {
+void CrostiniInstaller::OnAvailableDiskSpace(absl::optional<int64_t> bytes) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // |Cancel()| might be called immediately after |Install()|.
@@ -608,7 +611,8 @@
 
   DCHECK_EQ(installing_state_, InstallerState::kStart);
 
-  free_disk_space_ = bytes;
+  if (bytes.has_value())
+    free_disk_space_ = bytes.value();
   // Don't enforce minimum disk size on dev box or trybots because
   // base::SysInfo::AmountOfFreeDiskSpace returns zero in testing.
   if (base::SysInfo::IsRunningOnChromeOS() &&
diff --git a/chrome/browser/ash/crostini/crostini_installer.h b/chrome/browser/ash/crostini/crostini_installer.h
index 75ed674..87a6748 100644
--- a/chrome/browser/ash/crostini/crostini_installer.h
+++ b/chrome/browser/ash/crostini/crostini_installer.h
@@ -132,7 +132,7 @@
   void RecordSetupResult(SetupResult result);
 
   void OnCrostiniRestartFinished(crostini::CrostiniResult result);
-  void OnAvailableDiskSpace(int64_t bytes);
+  void OnAvailableDiskSpace(absl::optional<int64_t> bytes);
 
   void OnCrostiniRemovedAfterConfigurationFailed(
       crostini::CrostiniResult result);
diff --git a/chrome/browser/ash/crostini/crostini_installer_unittest.cc b/chrome/browser/ash/crostini/crostini_installer_unittest.cc
index 581b468..66bd028 100644
--- a/chrome/browser/ash/crostini/crostini_installer_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_installer_unittest.cc
@@ -28,6 +28,7 @@
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
 #include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h"
 #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
+#include "chromeos/ash/components/dbus/spaced/fake_spaced_client.h"
 #include "chromeos/ash/components/disks/disk_mount_manager.h"
 #include "chromeos/ash/components/disks/mock_disk_mount_manager.h"
 #include "content/public/test/browser_task_environment.h"
@@ -119,6 +120,8 @@
     ash::ChunneldClient::InitializeFake();
     ash::CiceroneClient::InitializeFake();
     ash::DebugDaemonClient::InitializeFake();
+    ash::FakeSpacedClient::InitializeFake();
+
     SetOSRelease();
     waiting_fake_concierge_client_ =
         new WaitingFakeConciergeClient(ash::FakeCiceroneClient::Get());
@@ -158,6 +161,7 @@
     ash::CiceroneClient::Shutdown();
     ash::ChunneldClient::Shutdown();
     ash::DlcserviceClient::Shutdown();
+    ash::FakeSpacedClient::Shutdown();
 
     browser_part_.ShutdownCrosComponentManager();
     component_manager_.reset();
diff --git a/chrome/browser/ash/crostini/crostini_util.cc b/chrome/browser/ash/crostini/crostini_util.cc
index fd37428a..0ac7a0d5 100644
--- a/chrome/browser/ash/crostini/crostini_util.cc
+++ b/chrome/browser/ash/crostini/crostini_util.cc
@@ -63,7 +63,8 @@
     "https://storage.googleapis.com/cros-containers/%d";
 const char kCrostiniDlcName[] = "termina-dlc";
 
-const base::FilePath::CharType kHomeDirectory[] = FILE_PATH_LITERAL("/home");
+const base::FilePath::CharType kHomeDirectory[] =
+    FILE_PATH_LITERAL("/home/chronos/user");
 
 namespace {
 
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
index 1eef8ce..70c23950 100644
--- a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
+++ b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
@@ -15,6 +15,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
 
 namespace ash {
 namespace eche_app {
@@ -29,7 +30,7 @@
     const std::u16string& message,
     const ui::ImageModel& icon,
     const message_center::RichNotificationData& rich_notification_data,
-    message_center::NotificationDelegate* delegate) {
+    scoped_refptr<message_center::NotificationDelegate> delegate) {
   return std::make_unique<message_center::Notification>(
       message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, id, title,
       message, icon, std::u16string() /* display_source */,
@@ -81,8 +82,10 @@
           kEcheAppRetryConnectionNotifierId,
           NotificationCatalogName::kEcheAppRetryConnection, title.value(),
           message.value(), ui::ImageModel(), rich_notification_data,
-          new NotificationDelegate(kEcheAppRetryConnectionNotifierId,
-                                   weak_ptr_factory_.GetWeakPtr())));
+          base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+              base::BindRepeating(
+                  &EcheAppNotificationController::LaunchTryAgain,
+                  weak_ptr_factory_.GetWeakPtr()))));
     } else if (web_type == mojom::WebNotificationType::DEVICE_IDLE) {
       message_center::RichNotificationData rich_notification_data;
       rich_notification_data.buttons.push_back(
@@ -92,8 +95,10 @@
           kEcheAppInactivityNotifierId,
           NotificationCatalogName::kEcheAppInactivity, title.value(),
           message.value(), ui::ImageModel(), rich_notification_data,
-          new NotificationDelegate(kEcheAppInactivityNotifierId,
-                                   weak_ptr_factory_.GetWeakPtr())));
+          base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+              base::BindRepeating(
+                  &EcheAppNotificationController::LaunchTryAgain,
+                  weak_ptr_factory_.GetWeakPtr()))));
     } else if (web_type == mojom::WebNotificationType::WIFI_NOT_READY) {
       message_center::RichNotificationData rich_notification_data;
       // Reuse the setting string for Eche's setting button.
@@ -103,8 +108,10 @@
           kEcheAppNetworkSettingNotifierId,
           NotificationCatalogName::kEcheAppNetworkSetting, title.value(),
           message.value(), ui::ImageModel(), rich_notification_data,
-          new NotificationDelegate(kEcheAppNetworkSettingNotifierId,
-                                   weak_ptr_factory_.GetWeakPtr())));
+          base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+              base::BindRepeating(
+                  &EcheAppNotificationController::LaunchNetworkSettings,
+                  weak_ptr_factory_.GetWeakPtr()))));
     } else {
       // No need to take the action.
       ShowNotification(CreateNotification(
@@ -112,8 +119,9 @@
           NotificationCatalogName::kEcheAppFromWebWithoutButton, title.value(),
           message.value(), ui::ImageModel(),
           message_center::RichNotificationData(),
-          new NotificationDelegate(kEcheAppFromWebWithoutButtonNotifierId,
-                                   weak_ptr_factory_.GetWeakPtr())));
+          base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+              message_center::HandleNotificationClickDelegate::
+                  ButtonClickCallback(base::DoNothing()))));
     }
   } else {
     PA_LOG(ERROR)
@@ -132,8 +140,9 @@
                                  title),
       l10n_util::GetStringUTF16(IDS_ECHE_APP_SCREEN_LOCK_NOTIFICATION_MESSAGE),
       ui::ImageModel(), rich_notification_data,
-      new NotificationDelegate(kEcheAppScreenLockNotifierId,
-                               weak_ptr_factory_.GetWeakPtr())));
+      base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+          base::BindRepeating(&EcheAppNotificationController::LaunchSettings,
+                              weak_ptr_factory_.GetWeakPtr()))));
 }
 
 void EcheAppNotificationController::ShowNotification(
@@ -163,37 +172,5 @@
       NotificationHandler::Type::TRANSIENT, kEcheAppNetworkSettingNotifierId);
 }
 
-EcheAppNotificationController::NotificationDelegate::NotificationDelegate(
-    const std::string& notification_id,
-    const base::WeakPtr<EcheAppNotificationController>& notification_controller)
-    : notification_id_(notification_id),
-      notification_controller_(notification_controller) {}
-
-EcheAppNotificationController::NotificationDelegate::~NotificationDelegate() {}
-void EcheAppNotificationController::NotificationDelegate::Click(
-    const absl::optional<int>& button_index,
-    const absl::optional<std::u16string>& reply) {
-  if (!button_index)
-    return;
-
-  if (notification_id_ == kEcheAppScreenLockNotifierId) {
-    if (*button_index == 0) {
-      notification_controller_->LaunchSettings();
-    }
-  } else if (notification_id_ == kEcheAppRetryConnectionNotifierId) {
-    if (*button_index == 0) {
-      notification_controller_->LaunchTryAgain();
-    }
-  } else if (notification_id_ == kEcheAppInactivityNotifierId) {
-    if (*button_index == 0) {
-      notification_controller_->LaunchTryAgain();
-    }
-  } else if (notification_id_ == kEcheAppNetworkSettingNotifierId) {
-    if (*button_index == 0) {
-      notification_controller_->LaunchNetworkSettings();
-    }
-  }
-}
-
 }  // namespace eche_app
 }  // namespace ash
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller.h b/chrome/browser/ash/eche_app/eche_app_notification_controller.h
index 3cc4fb1..5257b412 100644
--- a/chrome/browser/ash/eche_app/eche_app_notification_controller.h
+++ b/chrome/browser/ash/eche_app/eche_app_notification_controller.h
@@ -10,7 +10,6 @@
 #include "chrome/browser/ash/eche_app/eche_app_manager_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/message_center/public/cpp/notification.h"
-#include "ui/message_center/public/cpp/notification_delegate.h"
 
 class Profile;
 
@@ -49,27 +48,6 @@
  private:
   friend class EcheAppNotificationControllerTest;
 
-  // NotificationDelegate implementation for handling click events.
-  class NotificationDelegate : public message_center::NotificationDelegate {
-   public:
-    NotificationDelegate(const std::string& notification_id,
-                         const base::WeakPtr<EcheAppNotificationController>&
-                             notification_controller);
-
-    NotificationDelegate(const NotificationDelegate&) = delete;
-    NotificationDelegate& operator=(const NotificationDelegate&) = delete;
-
-    // message_center::NotificationDelegate:
-    void Click(const absl::optional<int>& button_index,
-               const absl::optional<std::u16string>& reply) override;
-
-   private:
-    ~NotificationDelegate() override;
-
-    std::string notification_id_;
-    base::WeakPtr<EcheAppNotificationController> notification_controller_;
-  };
-
   virtual void LaunchSettings();
   virtual void LaunchTryAgain();
   virtual void LaunchNetworkSettings();
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc b/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc
index 16e5004..7cebc0f 100644
--- a/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc
+++ b/chrome/browser/ash/eche_app/eche_app_notification_controller_unittest.cc
@@ -113,7 +113,7 @@
 
   // Clicking the notification button should relaunch again.
   EXPECT_CALL(*notification_controller_, LaunchTryAgain());
-  notification->delegate()->Click(0, absl::nullopt);
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
 
   title = u"Connection Lost Title";
   message = u"Connection Lost Message";
@@ -129,7 +129,7 @@
 
   // Clicking the notification button should relaunch again.
   EXPECT_CALL(*notification_controller_, LaunchTryAgain());
-  notification->delegate()->Click(0, absl::nullopt);
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
 
   title = u"Inactivity Title";
   message = u"Inactivity Message";
@@ -145,7 +145,7 @@
 
   // Clicking the first notification button should relaunch again.
   EXPECT_CALL(*notification_controller_, LaunchTryAgain());
-  notification->delegate()->Click(0, absl::nullopt);
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
 
   title = u"Check WIFI Title";
   message = u"Check WIFI Message";
@@ -161,7 +161,7 @@
 
   // Clicking the notification button should launch network settings.
   EXPECT_CALL(*notification_controller_, LaunchNetworkSettings());
-  notification->delegate()->Click(0, absl::nullopt);
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
 }
 
 TEST_F(EcheAppNotificationControllerTest, ShowScreenLockNotification) {
@@ -177,7 +177,7 @@
 
   // Clicking the notification button should launch settings.
   EXPECT_CALL(*notification_controller_, LaunchSettings());
-  notification->delegate()->Click(0, absl::nullopt);
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
 }
 
 TEST_F(EcheAppNotificationControllerTest,
@@ -195,7 +195,7 @@
 
   // Clicking the notification button should launch settings.
   EXPECT_CALL(*notification_controller_, LaunchSettings());
-  notification->delegate()->Click(0, absl::nullopt);
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
 }
 
 TEST_F(EcheAppNotificationControllerTest, CloseNotification) {
diff --git a/chrome/browser/ash/input_method/longpress_diacritics_suggester.cc b/chrome/browser/ash/input_method/longpress_diacritics_suggester.cc
index a420e6c..0940ce7 100644
--- a/chrome/browser/ash/input_method/longpress_diacritics_suggester.cc
+++ b/chrome/browser/ash/input_method/longpress_diacritics_suggester.cc
@@ -134,10 +134,11 @@
   }
 
   size_t new_index = 0;
-
+  bool move_next = false;
   switch (code) {
     case kDismissDomCode:
       DismissSuggestion();
+      RecordActionMetric(IMEPKLongpressDiacriticAction::kDismiss);
       return SuggestionStatus::kDismiss;
     case kAcceptDomCode:
       if (highlighted_index_.has_value()) {
@@ -146,15 +147,16 @@
       }
       return SuggestionStatus::kNotHandled;
     case kNextDomCode:
+    case kTabDomCode:
     case kPreviousDomCode:
+      move_next = (code == kNextDomCode || code == kTabDomCode);
       if (highlighted_index_ == absl::nullopt) {
         // We want the cursor to start at the end if you press back, and at the
         // beginning if you press next.
-        new_index =
-            (code == kNextDomCode) ? 0 : GetCurrentShownDiacritics().size() - 1;
+        new_index = move_next ? 0 : GetCurrentShownDiacritics().size() - 1;
       } else {
         SetButtonHighlighted(*highlighted_index_, false);
-        if (code == kNextDomCode) {
+        if (move_next) {
           new_index =
               (*highlighted_index_ + 1) % GetCurrentShownDiacritics().size();
         } else {
@@ -185,6 +187,7 @@
 
       // Dismiss on any unexpected key events.
       DismissSuggestion();
+      RecordActionMetric(IMEPKLongpressDiacriticAction::kDismiss);
       // NotHandled is passed so that the IME will let the key event pass
       // through.
       return SuggestionStatus::kNotHandled;
@@ -246,7 +249,6 @@
     LOG(ERROR) << "Failed to dismiss suggestion. " << error;
     return;
   }
-  RecordActionMetric(IMEPKLongpressDiacriticAction::kDismiss);
   Reset();
   return;
 }
diff --git a/chrome/browser/ash/input_method/longpress_diacritics_suggester.h b/chrome/browser/ash/input_method/longpress_diacritics_suggester.h
index 0f6a1a7..777ba1d1 100644
--- a/chrome/browser/ash/input_method/longpress_diacritics_suggester.h
+++ b/chrome/browser/ash/input_method/longpress_diacritics_suggester.h
@@ -26,6 +26,7 @@
 constexpr ui::DomCode kPreviousDomCode = ui::DomCode::ARROW_LEFT;
 constexpr ui::DomCode kAcceptDomCode = ui::DomCode::ENTER;
 constexpr ui::DomCode kDismissDomCode = ui::DomCode::ESCAPE;
+constexpr ui::DomCode kTabDomCode = ui::DomCode::TAB;
 
 // TODO(b/217560706): Replace diacritics with final set after research is
 // done (on a per input method engine basis).
diff --git a/chrome/browser/ash/input_method/longpress_diacritics_suggester_unittest.cc b/chrome/browser/ash/input_method/longpress_diacritics_suggester_unittest.cc
index 62eab527..868cafa 100644
--- a/chrome/browser/ash/input_method/longpress_diacritics_suggester_unittest.cc
+++ b/chrome/browser/ash/input_method/longpress_diacritics_suggester_unittest.cc
@@ -196,6 +196,28 @@
                 GetParam().candidates[expected_candidate_index]));
 }
 
+TEST_P(LongpressDiacriticsSuggesterTest, HighlightIncrementsOnTabKeyEvent) {
+  size_t expected_candidate_index = 2 % GetParam().candidates.size();
+  FakeSuggestionHandler suggestion_handler;
+  LongpressDiacriticsSuggester suggester =
+      LongpressDiacriticsSuggester(&suggestion_handler);
+  suggester.OnFocus(kContextId);
+
+  suggester.TrySuggestOnLongpress(GetParam().longpress_char);
+  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::TAB));
+  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::TAB));
+  suggester.HandleKeyEvent(CreateKeyEventFromCode(ui::DomCode::TAB));
+
+  EXPECT_EQ(suggestion_handler.GetContextId(), kContextId);
+  EXPECT_TRUE(suggestion_handler.GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler.GetSuggestionText(),
+            Join(GetParam().candidates));
+  EXPECT_EQ(suggestion_handler.GetHighlightedButton(),
+            CreateDiacriticsButtonFor(
+                expected_candidate_index,
+                GetParam().candidates[expected_candidate_index]));
+}
+
 TEST_P(LongpressDiacriticsSuggesterTest,
        HighlightDecrementsOnPreviousKeyEvent) {
   FakeSuggestionHandler suggestion_handler;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index f89ada1..623912dd 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -312,7 +312,7 @@
         <include name="IDR_LOCAL_WEB_APPROVALS_AFTER_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\local_web_approvals_after.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_CONTROLLER_JS" file="resources\chromeos\parent_access\parent_access_controller.js" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_UI_HANDLER_JS" file="resources\chromeos\parent_access\parent_access_ui_handler.js" type="BINDATA" />
-        <include name="IDR_PARENT_ACCESS_UI_MOJOM_WEBUI_JS" file="${root_gen_dir}\mojom-webui\chrome\browser\ui\webui\chromeos\parent_access\parent_access_ui.mojom-webui.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PARENT_ACCESS_UI_MOJOM_WEBUI_JS" file="${root_gen_dir}\mojom-webui\chrome\browser\ui\webui\ash\parent_access\parent_access_ui.mojom-webui.js" use_base_dir="false" type="BINDATA" />
       </if>
       <if expr="safe_browsing_mode == 1">
         <include name="IDR_RESET_PASSWORD_HTML" file="resources\reset_password\reset_password.html" type="BINDATA" />
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index a4f2359..847478b 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -271,6 +271,8 @@
 #include "chrome/browser/ui/webui/ash/launcher_internals/launcher_internals.mojom.h"
 #include "chrome/browser/ui/webui/ash/launcher_internals/launcher_internals_ui.h"
 #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/internet_config_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h"
@@ -278,8 +280,6 @@
 #include "chrome/browser/ui/webui/chromeos/manage_mirrorsync/manage_mirrorsync.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/manage_mirrorsync/manage_mirrorsync_ui.h"
 #include "chrome/browser/ui/webui/chromeos/network_ui.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/vm/vm.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/vm/vm_ui.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
@@ -1105,8 +1105,7 @@
       ash::multidevice_setup::MultiDeviceSetupDialogUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
-      parent_access_ui::mojom::ParentAccessUIHandler, chromeos::ParentAccessUI>(
-      map);
+      parent_access_ui::mojom::ParentAccessUIHandler, ash::ParentAccessUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
       ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter,
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
index 5111e59c..d67f3e6 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
@@ -436,6 +436,13 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemProviderServiceWorkerApiTest, Unmount) {
+  ASSERT_TRUE(RunExtensionTest("file_system_provider/service_worker/unmount",
+                               {.extension_url = "test.html"},
+                               {.load_as_component = true}))
+      << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemProviderServiceWorkerApiTest, WriteFile) {
   ASSERT_TRUE(RunExtensionTest("file_system_provider/service_worker/write_file",
                                {.extension_url = "test.html"},
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index c4bc61d..5b81e2d 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -43,9 +43,11 @@
 #include "extensions/common/error_utils.h"
 #include "extensions/common/feature_switch.h"
 #include "extensions/common/image_util.h"
+#include "extensions/common/manifest_constants.h"
 #include "extensions/common/mojom/view_type.mojom.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
+#include "url/origin.h"
 
 using content::WebContents;
 
@@ -499,10 +501,21 @@
   EXTENSION_FUNCTION_VALIDATE(details_);
   std::string* popup_string = details_->FindString("popup");
   EXTENSION_FUNCTION_VALIDATE(popup_string);
-
   GURL popup_url;
-  if (!popup_string->empty())
+
+  // If an empty string is passed, remove the explicitly set popup. Setting it
+  // back to an empty string (URL) will cause it to fall back to the default set
+  // in the manifest.
+  if (!popup_string->empty()) {
     popup_url = extension()->GetResourceURL(*popup_string);
+    // Validate popup is same-origin (only for this extension). We do not
+    // validate the file exists (like we do in manifest validation) because an
+    // extension could potentially intercept the request with a service worker
+    // and dynamically provide content.
+    if (!extension()->origin().IsSameOriginWith(popup_url)) {
+      return RespondNow(Error(manifest_errors::kInvalidExtensionOriginPopup));
+    }
+  }
 
   extension_action_->SetPopupUrl(tab_id_, popup_url);
   NotifyChange();
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
index 723e2de..b7248eb 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -37,6 +37,7 @@
 #include "extensions/browser/state_store.h"
 #include "extensions/common/api/extension_action/action_info_test_util.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/manifest_constants.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
@@ -1076,8 +1077,9 @@
   console_observer.Wait();
 }
 
-// Tests calling setIcon() in the service worker with an invalid icon path
-// specified. Regression test for https://crbug.com/1262029.
+// Tests calling setIcon() in the service worker with an invalid icon paths
+// specified. Regression test for https://crbug.com/1262029. Regression test for
+// https://crbug.com/1372518.
 IN_PROC_BROWSER_TEST_F(ExtensionActionAPITest, SetIconInWorkerWithInvalidPath) {
   constexpr char kManifestTemplate[] =
       R"({
@@ -1090,16 +1092,34 @@
 
   constexpr char kBackgroundJs[] =
       R"(let expectedError = "%s";
+         const singlePath = 'does_not_exist.png';
+         const multiplePaths = {
+           16: 'does_not_exist.png',
+           32: 'also_does_not_exist.png'
+         };
+
          chrome.test.runTests([
-           function withCallback() {
-             chrome.action.setIcon({path: 'does_not_exist.png'}, () => {
+           function singleWithCallback() {
+             chrome.action.setIcon({path: singlePath}, () => {
                chrome.test.assertLastError(expectedError);
                chrome.test.succeed();
              });
            },
-           async function withPromise() {
+           async function singleWithPromise() {
              await chrome.test.assertPromiseRejects(
-                 chrome.action.setIcon({path: 'does_not_exist.png'}),
+                 chrome.action.setIcon({path: singlePath}),
+                 'Error: ' + expectedError);
+             chrome.test.succeed();
+           },
+           function multipleWithCallback() {
+             chrome.action.setIcon({path: multiplePaths}, () => {
+               chrome.test.assertLastError(expectedError);
+               chrome.test.succeed();
+             });
+           },
+           async function multipleWithPromise() {
+             await chrome.test.assertPromiseRejects(
+                 chrome.action.setIcon({path: multiplePaths}),
                  'Error: ' + expectedError);
              chrome.test.succeed();
            }
@@ -1122,6 +1142,86 @@
   EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
 }
 
+// Tests multiple cases of setting an invalid popup that violate same-origin
+// checks.
+IN_PROC_BROWSER_TEST_P(MultiActionAPITest, SetPopupWithInvalidPath) {
+  constexpr char kManifestTemplate[] =
+      R"({
+           "name": "Invalid Popup Path",
+           "manifest_version": %d,
+           "version": "0.1",
+           "%s": {}
+         })";
+  constexpr char kSetPopupJsTemplate[] =
+      R"(
+        function setPopup(details, expectedError) {
+          chrome.%s.setPopup(details, () => {
+            chrome.test.assertLastError(expectedError);
+            chrome.test.succeed();
+          });
+        };
+      )";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(base::StringPrintf(
+      kManifestTemplate, GetManifestVersionForActionType(GetParam()),
+      GetManifestKeyForActionType(GetParam())));
+  test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"),
+                     "// This space left blank.");
+  test_dir.WriteFile(FILE_PATH_LITERAL("page.html"), kPageHtmlTemplate);
+  test_dir.WriteFile(FILE_PATH_LITERAL("page.js"),
+                     base::StringPrintf(kSetPopupJsTemplate,
+                                        GetAPINameForActionType(GetParam())));
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+  ExtensionAction* action = GetExtensionAction(*extension);
+  ASSERT_TRUE(action);
+
+  auto get_script = [](int tab_id, const char* popup_input) {
+    constexpr char kSetPopup[] = R"(setPopup({tabId: %d, popup: '%s'}, "%s");)";
+    return base::StringPrintf(kSetPopup, tab_id, popup_input,
+                              manifest_errors::kInvalidExtensionOriginPopup);
+  };
+
+  content::RenderFrameHost* navigated_host = ui_test_utils::NavigateToURL(
+      browser(), extension->GetResourceURL("page.html"));
+  ASSERT_TRUE(navigated_host);
+  content::WebContents* web_contents = GetActiveTab();
+  int tab_id = GetActiveTabId();
+
+  // Set the popup to an invalid nonexistent extension URL and expect an error.
+  {
+    static constexpr char kInvalidPopupUrl[] =
+        "chrome-extension://notavalidextensionid/popup.html";
+    RunTestAndWaitForSuccess(web_contents,
+                             get_script(tab_id, kInvalidPopupUrl));
+  }
+
+  // Set the popup to a web URL and expect an error.
+  {
+    static constexpr char kWebUrl[] = "http://test.com";
+    RunTestAndWaitForSuccess(web_contents, get_script(tab_id, kWebUrl));
+  }
+
+  // Set the popup to another existing extension and expect an error.
+  {
+    TestExtensionDir different_extension_dir;
+    different_extension_dir.WriteManifest(base::StringPrintf(
+        kManifestTemplate, GetManifestVersionForActionType(GetParam()),
+        GetManifestKeyForActionType(GetParam())));
+    different_extension_dir.WriteFile(FILE_PATH_LITERAL("popup.html"),
+                                      "// This space left blank.");
+    const Extension* different_extension =
+        LoadExtension(different_extension_dir.UnpackedPath());
+    ASSERT_TRUE(different_extension);
+    const std::string different_extension_popup_url =
+        different_extension->GetResourceURL("popup.html").spec();
+    RunTestAndWaitForSuccess(
+        web_contents,
+        get_script(tab_id, different_extension_popup_url.c_str()));
+  }
+}
+
 // Tests various getter and setter methods.
 IN_PROC_BROWSER_TEST_P(MultiActionAPITest, GettersAndSetters) {
   // Load up an extension with default values.
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
index 65511e30..69579bf1 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -34,7 +34,9 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.feed.v2.FeedUserActionType;
+import org.chromium.chrome.browser.feed.webfeed.WebFeedAvailabilityStatus;
 import org.chromium.chrome.browser.feed.webfeed.WebFeedBridge;
+import org.chromium.chrome.browser.feed.webfeed.WebFeedSnackbarController;
 import org.chromium.chrome.browser.feed.webfeed.WebFeedSubscriptionRequestStatus;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -286,21 +288,29 @@
                 Log.i(TAG, "Invalid webFeedName", e);
                 return;
             }
+            WebFeedFollowUpdate.Callback updateCallback = update.callback();
             if (update.isFollow()) {
-                WebFeedBridge.followFromId(
-                        webFeedId, update.isDurable(), update.webFeedChangeReason(), results -> {
-                            WebFeedFollowUpdate.Callback callback = update.callback();
-                            if (callback != null) {
-                                callback.requestComplete(results.requestStatus
-                                        == WebFeedSubscriptionRequestStatus.SUCCESS);
-                            }
-                        });
+                Callback<WebFeedBridge.FollowResults> followCallback = results -> {
+                    boolean successfulFollow =
+                            results.requestStatus == WebFeedSubscriptionRequestStatus.SUCCESS;
+                    if (updateCallback != null) {
+                        updateCallback.requestComplete(successfulFollow);
+                    }
+                    if (successfulFollow && results.metadata != null) {
+                        mWebFeedSnackbarController.showPostSuccessfulFollowHelp(
+                                results.metadata.title,
+                                results.metadata.availabilityStatus
+                                        == WebFeedAvailabilityStatus.ACTIVE,
+                                mStreamKind);
+                    }
+                };
+                WebFeedBridge.followFromId(webFeedId, update.isDurable(),
+                        update.webFeedChangeReason(), followCallback);
             } else {
                 WebFeedBridge.unfollow(
                         webFeedId, update.isDurable(), update.webFeedChangeReason(), results -> {
-                            WebFeedFollowUpdate.Callback callback = update.callback();
-                            if (callback != null) {
-                                callback.requestComplete(results.requestStatus
+                            if (updateCallback != null) {
+                                updateCallback.requestComplete(results.requestStatus
                                         == WebFeedSubscriptionRequestStatus.SUCCESS);
                             }
                         });
@@ -572,6 +582,9 @@
     private final FeedAutoplaySettingsDelegate mFeedAutoplaySettingsDelegate;
     private UnreadContentObserver mUnreadContentObserver;
     FeedContentFirstLoadWatcher mFeedContentFirstLoadWatcher;
+    // Snackbar (and post-Follow dialog) controller used exclusively for handling in-feed
+    // post-Follow and post-Unfollow UX.
+    WebFeedSnackbarController mWebFeedSnackbarController;
 
     // For loading more content.
     private int mAccumulatedDySinceLastLoadMore;
@@ -627,8 +640,9 @@
             WindowAndroid windowAndroid, Supplier<ShareDelegate> shareDelegateSupplier,
             int streamKind, FeedAutoplaySettingsDelegate feedAutoplaySettingsDelegate,
             FeedActionDelegate actionDelegate, HelpAndFeedbackLauncher helpAndFeedbackLauncher,
-            FeedContentFirstLoadWatcher feedContentFirstLoadWatcher) {
-        this.mActivity = activity;
+            FeedContentFirstLoadWatcher feedContentFirstLoadWatcher,
+            Stream.StreamsMediator streamsMediator) {
+        mActivity = activity;
         mStreamKind = streamKind;
         mReliabilityLoggingBridge = new FeedReliabilityLoggingBridge();
         mNativeFeedStream = FeedStreamJni.get().init(
@@ -643,6 +657,12 @@
         mFeedAutoplaySettingsDelegate = feedAutoplaySettingsDelegate;
         mRotationObserver = new RotationObserver();
         mFeedContentFirstLoadWatcher = feedContentFirstLoadWatcher;
+        WebFeedSnackbarController.FeedLauncher switchToFollowing = () -> {
+            // Note: for now there's no need to store streamsMediator as an instance variable.
+            streamsMediator.switchToStreamKind(StreamKind.FOLLOWING);
+        };
+        mWebFeedSnackbarController = new WebFeedSnackbarController(activity, switchToFollowing,
+                windowAndroid.getModalDialogManager(), snackbarManager);
 
         mHandlersMap = new HashMap<>();
         mHandlersMap.put(SurfaceActionsHandler.KEY, new FeedSurfaceActionsHandler(actionDelegate));
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
index 59d9012..47174cf 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
@@ -154,6 +154,8 @@
     private WebFeedFollowUpdate.Callback mWebFeedFollowUpdateCallback;
     @Mock
     private FeedContentFirstLoadWatcher mFeedContentFirstLoadWatcher;
+    @Mock
+    private Stream.StreamsMediator mStreamsMediator;
 
     @Rule
     public JniMocker mocker = new JniMocker();
@@ -190,7 +192,7 @@
                 /* isPlaceholderShown= */ false, mWindowAndroid, mShareDelegateSupplier,
                 /* isInterestFeed= */ StreamKind.FOR_YOU,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
-                /*helpAndFeedbackLauncher=*/null, mFeedContentFirstLoadWatcher);
+                /*helpAndFeedbackLauncher=*/null, mFeedContentFirstLoadWatcher, mStreamsMediator);
         mFeedStream.mMakeGURL = url -> JUnitTestGURLs.getGURL(url);
         mRecyclerView = new RecyclerView(mActivity);
         mRecyclerView.setAdapter(mAdapter);
@@ -1010,7 +1012,7 @@
                 /* isInterestFeed= */ StreamKind.FOLLOWING,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         mFeedStream = stream;
         createHeaderContent(1);
         bindToView();
@@ -1031,7 +1033,7 @@
                 /* isInterestFeed= */ StreamKind.FOR_YOU,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertNull(stream.getUnreadContentObserverForTest());
     }
 
@@ -1046,7 +1048,7 @@
                 /* isInterestFeed= */ StreamKind.FOLLOWING,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertNotNull(stream.getUnreadContentObserverForTest());
         FeatureList.setTestFeatures(null);
     }
@@ -1062,7 +1064,7 @@
                 StreamKind.FOLLOWING,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertNotNull(stream.getUnreadContentObserverForTest());
         FeatureList.setTestFeatures(null);
     }
@@ -1078,7 +1080,7 @@
                 StreamKind.FOR_YOU,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertFalse(stream.supportsOptions());
     }
 
@@ -1093,7 +1095,7 @@
                 StreamKind.FOR_YOU,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertFalse(stream.supportsOptions());
     }
 
@@ -1108,7 +1110,7 @@
                 StreamKind.FOLLOWING,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertFalse(stream.supportsOptions());
     }
 
@@ -1123,7 +1125,7 @@
                 StreamKind.FOLLOWING,
                 /* FeedAutoplaySettingsDelegate= */ null, mActionDelegate,
                 /*helpAndFeedbackLauncher=*/null,
-                /*FeedContentFirstLoadWatcher=*/null);
+                /*FeedContentFirstLoadWatcher=*/null, /*Stream.StreamsMediator*/ null);
         assertTrue(stream.supportsOptions());
     }
 
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java
index 8e5948b6..40a76b9 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java
@@ -19,6 +19,16 @@
 
 /** Interface used for interacting with the Stream library in order to render a stream of cards. */
 public interface Stream {
+    /**
+     * The mediator of multiple Streams.
+     */
+    public interface StreamsMediator {
+        /**
+         * Allows the switching to another Stream.
+         * @param streamKind The {@link StreamKind} of the stream to switch to.
+         */
+        void switchToStreamKind(@StreamKind int streamKind);
+    }
     /** Called when the Stream is no longer needed. */
     default void destroy() {}
 
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedDialogCoordinator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedDialogCoordinator.java
index 0b28442..7519928 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedDialogCoordinator.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedDialogCoordinator.java
@@ -13,6 +13,7 @@
 import android.view.View;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
@@ -60,14 +61,45 @@
      *
      * @param context The {@link Context}.
      * @param feedLauncher {@link FeedLauncher} for launching the NTP.
-     * @param title The title of the site that was just followed.
+     * @param title The title of the Web Feed that was just followed.
      * @param isActive Whether the followed site is active (has content available).
      */
     void initialize(Context context, FeedLauncher feedLauncher, String title, boolean isActive) {
+        Runnable positiveAction = () -> {
+            FeedServiceBridge.reportOtherUserAction(StreamKind.UNKNOWN,
+                    FeedUserActionType.TAPPED_GO_TO_FEED_POST_FOLLOW_ACTIVE_HELP);
+            feedLauncher.openFollowingFeed();
+        };
+        initializeInternal(context, positiveAction,
+                R.string.web_feed_post_follow_dialog_go_to_following, title, isActive);
+    }
+
+    /**
+     * Initializes the {@link WebFeedDialogCoordinator}.
+     *
+     * @param context The {@link Context}.
+     * @param activeAction {@link Runnable} to execute for the primary when-active action.
+     * @param title The title of the Web Feed that was just followed.
+     * @param isActive Whether the followed site is active (has content available).
+     */
+    void initializeForInFollowingFollow(
+            Context context, @Nullable Runnable activeAction, String title, boolean isActive) {
+        Runnable positiveAction = () -> {
+            FeedServiceBridge.reportOtherUserAction(StreamKind.UNKNOWN,
+                    FeedUserActionType.TAPPED_GOT_IT_FEED_POST_FOLLOW_ACTIVE_HELP);
+            if (activeAction != null) activeAction.run();
+        };
+        initializeInternal(context, positiveAction, R.string.web_feed_post_follow_dialog_got_it,
+                title, isActive);
+    }
+
+    private void initializeInternal(Context context, Runnable positiveAction,
+            int positiveActionLabelId, String title, boolean isActive) {
         mContext = context;
         View webFeedDialogView =
                 LayoutInflater.from(context).inflate(R.layout.web_feed_dialog, null);
-        WebFeedDialogContents dialogContents = buildDialogContents(feedLauncher, isActive, title);
+        WebFeedDialogContents dialogContents =
+                buildDialogContents(positiveAction, positiveActionLabelId, title, isActive);
         PropertyModel model = buildModel(dialogContents);
         mMediator.initialize(webFeedDialogView, dialogContents);
         PropertyModelChangeProcessor.create(
@@ -79,7 +111,7 @@
     }
 
     private WebFeedDialogContents buildDialogContents(
-            FeedLauncher feedLauncher, boolean isActive, String title) {
+            Runnable positiveAction, int positiveActionLabelId, String title, boolean isActive) {
         RecordHistogram.recordEnumeratedHistogram(
                 "ContentSuggestions.Feed.WebFeed.PostFollowDialog.Show",
                 isActive ? WebFeedPostFollowDialogPresentation.AVAILABLE
@@ -93,14 +125,11 @@
         if (isActive) {
             description = mContext.getString(
                     R.string.web_feed_post_follow_dialog_stories_ready_description, title);
-            primaryButtonText =
-                    mContext.getString(R.string.web_feed_post_follow_dialog_open_a_new_tab);
+            primaryButtonText = mContext.getString(positiveActionLabelId);
             secondaryButtonText = mContext.getString(R.string.close);
             buttonClickCallback = dismissalCause -> {
                 if (dismissalCause.equals(DialogDismissalCause.POSITIVE_BUTTON_CLICKED)) {
-                    FeedServiceBridge.reportOtherUserAction(StreamKind.UNKNOWN,
-                            FeedUserActionType.TAPPED_GO_TO_FEED_POST_FOLLOW_ACTIVE_HELP);
-                    feedLauncher.openFollowingFeed();
+                    positiveAction.run();
                 } else {
                     FeedServiceBridge.reportOtherUserAction(StreamKind.UNKNOWN,
                             FeedUserActionType.TAPPED_DISMISS_POST_FOLLOW_ACTIVE_HELP);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarController.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarController.java
index 3b4af2e..c4e2d53 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarController.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarController.java
@@ -6,6 +6,8 @@
 
 import android.content.Context;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.feed.FeedServiceBridge;
 import org.chromium.chrome.browser.feed.FeedSurfaceTracker;
@@ -27,7 +29,7 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Controller for showing Web Feed snackbars.
+ * Controller for showing Web Feed snackbars or the post-Follow educational dialog.
  */
 public class WebFeedSnackbarController {
     /**
@@ -49,11 +51,11 @@
      * Constructs an instance of {@link WebFeedSnackbarController}.
      *
      * @param context The {@link Context} to retrieve strings for the snackbars.
-     * @param feedLauncher The {@link FeedLauncher} to launch the feed.
+     * @param feedLauncher The {@link FeedLauncher} to launch the Following feed.
      * @param dialogManager {@link ModalDialogManager} for managing the dialog.
      * @param snackbarManager {@link SnackbarManager} to manage the snackbars.
      */
-    WebFeedSnackbarController(Context context, FeedLauncher feedLauncher,
+    public WebFeedSnackbarController(Context context, FeedLauncher feedLauncher,
             ModalDialogManager dialogManager, SnackbarManager snackbarManager) {
         mContext = context;
         mFeedLauncher = feedLauncher;
@@ -63,15 +65,26 @@
 
     /**
      * Show appropriate post-follow snackbar/dialog depending on success/failure.
+     *
+     * @param tab The tab form which a URL-based follow was requested; can be null if followId is
+     *         valid.
+     * @param results The results of the follow request.
+     * @param followId The identifier of the attempted followed Web Feed, if known.
+     * @param url The URL that was attempted to be followed, if known.
+     * @param fallbackTitle the user-visible fallback title of the Web Feed, in case the request
+     *         returns no metadata.
+     * @param webFeedChangeReason enum value identifying the origin of the request.
      */
-    void showPostFollowHelp(Tab tab, WebFeedBridge.FollowResults results, byte[] followId, GURL url,
-            String fallbackTitle, int webFeedChangeReason) {
+    void showPostFollowHelp(@Nullable Tab tab, WebFeedBridge.FollowResults results,
+            @Nullable byte[] followId, @Nullable GURL url, String fallbackTitle,
+            int webFeedChangeReason) {
         if (results.requestStatus == WebFeedSubscriptionRequestStatus.SUCCESS) {
             if (results.metadata != null) {
                 showPostSuccessfulFollowHelp(results.metadata.title,
-                        results.metadata.availabilityStatus == WebFeedAvailabilityStatus.ACTIVE);
+                        results.metadata.availabilityStatus == WebFeedAvailabilityStatus.ACTIVE,
+                        StreamKind.UNKNOWN);
             } else {
-                showPostSuccessfulFollowHelp(fallbackTitle, /*isActive=*/false);
+                showPostSuccessfulFollowHelp(fallbackTitle, /*isActive=*/false, StreamKind.UNKNOWN);
             }
         } else {
             int failureMessage = R.string.web_feed_follow_generic_failure_snackbar_message;
@@ -107,13 +120,27 @@
         }
     }
 
-    private void showPostSuccessfulFollowHelp(String title, boolean isActive) {
+    /**
+     * Show snackcar/dialog after a successful follow request.
+     *
+     * @param title The user-visible title of the followed Web Feed.
+     * @param isActive True if the followed Web Feed is considered "active".
+     * @param followFromFeed Identifies if the Follow request was triggered from within a feed;
+     *         {@link StreamKind.UNKNOWN} meaning it was not from within any feeds.
+     */
+    public void showPostSuccessfulFollowHelp(
+            String title, boolean isActive, @StreamKind int followFromFeed) {
         if (TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
                         .shouldTriggerHelpUI(
                                 FeatureConstants.IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE)) {
-            mWebFeedDialogCoordinator.initialize(mContext, mFeedLauncher, title, isActive);
+            if (followFromFeed == StreamKind.FOLLOWING) {
+                mWebFeedDialogCoordinator.initializeForInFollowingFollow(
+                        mContext, null, title, isActive);
+            } else {
+                mWebFeedDialogCoordinator.initialize(mContext, mFeedLauncher, title, isActive);
+            }
             mWebFeedDialogCoordinator.showDialog();
-        } else {
+        } else if (followFromFeed != StreamKind.FOLLOWING) {
             SnackbarController snackbarController = new PinnedSnackbarController() {
                 @Override
                 public void onAction(Object actionData) {
@@ -124,7 +151,7 @@
             showSnackbar(
                     mContext.getString(R.string.web_feed_follow_success_snackbar_message, title),
                     snackbarController, Snackbar.UMA_WEB_FEED_FOLLOW_SUCCESS,
-                    R.string.web_feed_follow_success_snackbar_action);
+                    R.string.web_feed_follow_success_snackbar_action_go_to_following);
         }
     }
 
@@ -274,9 +301,9 @@
 
             if (!isFollowIdValid(mFollowId)) {
                 WebFeedBridge.followFromUrl(mPinnedTab, mUrl, mWebFeedChangeReason, result -> {
-                    byte[] mFollowId = result.metadata != null ? result.metadata.id : null;
+                    byte[] resultFollowId = result.metadata != null ? result.metadata.id : null;
                     showPostFollowHelp(
-                            mPinnedTab, result, mFollowId, mUrl, mTitle, mWebFeedChangeReason);
+                            mPinnedTab, result, resultFollowId, mUrl, mTitle, mWebFeedChangeReason);
                 });
             } else {
                 WebFeedBridge.followFromId(mFollowId, /*isDurable=*/false, mWebFeedChangeReason,
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarControllerTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarControllerTest.java
index 910986f..2ba9f65 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarControllerTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedSnackbarControllerTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.feed.webfeed;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -158,6 +159,29 @@
     }
 
     @Test
+    public void showPostSuccessfulFollowHelp_ShowsSnackbar_FromForYouFeed() {
+        mWebFeedSnackbarController.showPostSuccessfulFollowHelp(sTitle, true, StreamKind.FOR_YOU);
+
+        assertFalse("Dialog should not be showing.", mDialogManager.isShowing());
+        verify(mSnackbarManager).showSnackbar(mSnackbarCaptor.capture());
+        Snackbar snackbar = mSnackbarCaptor.getValue();
+        assertEquals("Snackbar should be for successful follow.",
+                Snackbar.UMA_WEB_FEED_FOLLOW_SUCCESS, snackbar.getIdentifierForTesting());
+        assertEquals("Snackbar message should be for successful follow with title from metadata.",
+                mContext.getString(R.string.web_feed_follow_success_snackbar_message,
+                        getSuccessfulFollowResult().metadata.title),
+                snackbar.getTextForTesting());
+    }
+
+    @Test
+    public void showPostSuccessfulFollowHelp_NoSnackbar_FromFollowingFeed() {
+        mWebFeedSnackbarController.showPostSuccessfulFollowHelp(sTitle, true, StreamKind.FOLLOWING);
+
+        assertFalse("Dialog should not be showing.", mDialogManager.isShowing());
+        verify(mSnackbarManager, times(0)).showSnackbar(any());
+    }
+
+    @Test
     public void showSnackbarForFollow_successful_noMetadata() {
         WebFeedBridge.FollowResults followResults = new WebFeedBridge.FollowResults(
                 WebFeedSubscriptionRequestStatus.SUCCESS, /*metadata=*/null);
@@ -214,6 +238,24 @@
     }
 
     @Test
+    public void showPostSuccessfulFollowHelp_Dialog_FromForYouFeed() {
+        when(mTracker.shouldTriggerHelpUI(FeatureConstants.IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE))
+                .thenReturn(true);
+        when(mTracker.shouldTriggerHelpUIWithSnooze(
+                     FeatureConstants.IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE))
+                .thenReturn(new TriggerDetails(true, false));
+
+        mWebFeedSnackbarController.showPostSuccessfulFollowHelp(sTitle, true, StreamKind.FOR_YOU);
+
+        View currentDialog =
+                mDialogManager.getCurrentDialogForTest().get(ModalDialogProperties.CUSTOM_VIEW);
+        verify(mSnackbarManager, times(0)).showSnackbar(any());
+        assertTrue("Dialog should be showing.", mDialogManager.isShowing());
+        // TODO(b/243676323): figure out how to test the positive_button label, which is out of the
+        // currentDialog hierarchy.
+    }
+
+    @Test
     public void showPromoDialogForFollow_successful_notActive() {
         when(mTracker.shouldTriggerHelpUI(FeatureConstants.IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE))
                 .thenReturn(true);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 71126c4..b1b4fd4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3485,21 +3485,11 @@
     "expiry_milestone": 90
   },
   {
-    "name": "exo-lock-notification",
-    "owners": [ "cpelling@google.com" ],
-    "expiry_milestone": 107
-  },
-  {
     "name": "exo-ordinal-motion",
     "owners": [ "hollingum@google.com" ],
     "expiry_milestone": 130
   },
   {
-    "name": "exo-pointer-lock",
-    "owners": [ "hollingum@google.com" ],
-    "expiry_milestone": 107
-  },
-  {
     "name": "expanded-tab-strip",
     "owners": [ "rkgibson@google.com", "gambard", "bling-flags@google.com" ],
     "expiry_milestone": 105
@@ -4160,11 +4150,6 @@
     "expiry_milestone": 121
   },
   {
-    "name": "initial-navigation-entry",
-    "owners": [ "rakina", "chrome-security-architecture@google.com" ],
-    "expiry_milestone": 105
-  },
-  {
     "name": "install-isolated-web-app-from-file",
     "owners": [ "kuragin", "reillyg", "rmcelrath" ],
     "expiry_milestone": 140
@@ -4433,7 +4418,7 @@
   {
     "name": "launcher-play-store-search",
     "owners": [ "amandadeacon", "tby", "wrong" ],
-    "expiry_milestone": 108
+    "expiry_milestone": 110
   },
   {
     "name": "legacy-tls-interstitial",
@@ -4834,7 +4819,7 @@
   {
     "name": "ntp-chrome-cart-module",
     "owners": [ "wychen", "tiborg", "yuezhanggg" ],
-    "expiry_milestone": 108
+    "expiry_milestone": 112
   },
   {
     "name": "ntp-desktop-lens",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 025f769b3..f6f6629 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1695,10 +1695,6 @@
 const char kInfobarScrollOptimizationDescription[] =
     "Optimize Infobar scroll on Android.";
 
-const char kInitialNavigationEntryName[] = "Initial NavigationEntry";
-const char kInitialNavigationEntryDescription[] =
-    "Enables creation of initial NavigationEntry on tab creation.";
-
 const char kInProductHelpDemoModeChoiceName[] = "In-Product Help Demo Mode";
 const char kInProductHelpDemoModeChoiceDescription[] =
     "Selects the In-Product Help demo mode.";
@@ -5415,11 +5411,6 @@
 const char kExoOrdinalMotionDescription[] =
     "Send unaccelerated values as raw motion events to linux applications.";
 
-const char kExoLockNotificationName[] = "Notification bubble for UI lock";
-const char kExoLockNotificationDescription[] =
-    "Show a notification bubble once an application has switched to "
-    "non-immersive fullscreen mode or obtained pointer lock.";
-
 const char kExperimentalAccessibilityDictationWithPumpkinName[] =
     "Experimental accessibility dictation using the pumpkin semantic parser.";
 const char kExperimentalAccessibilityDictationWithPumpkinDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 273d633..252d7d4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -942,9 +942,6 @@
 extern const char kInfobarScrollOptimizationName[];
 extern const char kInfobarScrollOptimizationDescription[];
 
-extern const char kInitialNavigationEntryName[];
-extern const char kInitialNavigationEntryDescription[];
-
 extern const char kInProductHelpDemoModeChoiceName[];
 extern const char kInProductHelpDemoModeChoiceDescription[];
 
@@ -3089,9 +3086,6 @@
 extern const char kExoOrdinalMotionName[];
 extern const char kExoOrdinalMotionDescription[];
 
-extern const char kExoLockNotificationName[];
-extern const char kExoLockNotificationDescription[];
-
 extern const char kExperimentalAccessibilityDictationWithPumpkinName[];
 extern const char kExperimentalAccessibilityDictationWithPumpkinDescription[];
 
diff --git a/chrome/browser/net/private_network_access_browsertest.cc b/chrome/browser/net/private_network_access_browsertest.cc
index 98108ee3..24ccb3e 100644
--- a/chrome/browser/net/private_network_access_browsertest.cc
+++ b/chrome/browser/net/private_network_access_browsertest.cc
@@ -1504,14 +1504,9 @@
 // load due to a transient network error, it is auto-reloaded a short while
 // later and that fetch is not blocked as a private network request.
 //
-// TODO(crbug.com/1326341): Flaky on Linux MSan.
-#if BUILDFLAG(IS_LINUX) && defined(MEMORY_SANITIZER)
-#define MAYBE_AutoReloadWorks DISABLED_AutoReloadWorks
-#else
-#define MAYBE_AutoReloadWorks AutoReloadWorks
-#endif
+// TODO(crbug.com/1326341): Test is flaky.
 IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessAutoReloadBrowserTest,
-                       MAYBE_AutoReloadWorks) {
+                       DISABLED_AutoReloadWorks) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL url = embedded_test_server()->GetURL("/defaultresponse");
 
diff --git a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
index 7afa4f8c..babcafe0 100644
--- a/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
+++ b/chrome/browser/page_load_metrics/integration_tests/largest_contentful_paint_browsertest.cc
@@ -274,6 +274,10 @@
 
 class IsAnimatedLCPTest : public MetricIntegrationTest {
  public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                    "LCPAnimatedImagesWebExposed");
+  }
   void test_is_animated(const char* html_name,
                         blink::LargestContentfulPaintType flag_set,
                         bool expected,
@@ -283,7 +287,8 @@
             web_contents());
     waiter->AddPageExpectation(page_load_metrics::PageLoadMetricsTestWaiter::
                                    TimingField::kLargestContentfulPaint);
-    waiter->AddMinimumCompleteResourcesExpectation(entries);
+    if (entries)
+      waiter->AddMinimumCompleteResourcesExpectation(entries);
     Start();
     Load(html_name);
     EXPECT_EQ(EvalJs(web_contents()->GetPrimaryMainFrame(), "run_test()").error,
@@ -320,6 +325,19 @@
                    /*expected=*/false);
 }
 
+// crbug.com/1373885: This test seems to be unreliable on Lacros
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#define MAYBE_LargestContentfulPaint_IsVideo \
+  DISABLED_LargestContentfulPaint_IsVideo
+#else
+#define MAYBE_LargestContentfulPaint_IsVideo LargestContentfulPaint_IsVideo
+#endif
+IN_PROC_BROWSER_TEST_F(IsAnimatedLCPTest,
+                       MAYBE_LargestContentfulPaint_IsVideo) {
+  test_is_animated("/is_video.html", blink::LargestContentfulPaintType::kVideo,
+                   /*expected=*/true, /*entries=*/0);
+}
+
 class MouseoverLCPTest : public MetricIntegrationTest {
  public:
   void test_mouseover(const char* html_name,
diff --git a/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc b/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
index 5297cc7..73483e1 100644
--- a/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
+++ b/chrome/browser/pdf/pdf_find_request_manager_browsertest.cc
@@ -245,8 +245,10 @@
   // Verify that find-in-page works fine.
   auto options = blink::mojom::FindOptions::New();
   Find("FXCMAP_CMap", options.Clone());
+  delegate()->WaitForFinalReply();
   options->new_session = false;
   Find("FXCMAP_CMap", options.Clone());
+  delegate()->WaitForFinalReply();
   Find("FXCMAP_CMap", options.Clone());
   delegate()->WaitForFinalReply();
 
diff --git a/chrome/browser/resources/chromeos/parent_access/BUILD.gn b/chrome/browser/resources/chromeos/parent_access/BUILD.gn
index 1e128fe..5a0f1f1d 100644
--- a/chrome/browser/resources/chromeos/parent_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/parent_access/BUILD.gn
@@ -14,7 +14,7 @@
   closure_flags = default_closure_args + mojom_js_args + [
                     "js_module_root=" + rebase_path(".", root_build_dir),
                     "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/chromeos/parent_access/",
+                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/ash/parent_access/",
                             root_build_dir),
                   ]
   deps = [
@@ -42,7 +42,7 @@
     ":parent_access_after",
     ":parent_access_ui",
     ":parent_access_ui_handler",
-    "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings_webui_js",
+    "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
   externs_list = [ "//ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager_externs.js" ]
@@ -59,7 +59,7 @@
   deps = [
     ":parent_access_controller",
     ":parent_access_ui_handler",
-    "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings_webui_js",
+    "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:load_time_data.m",
   ]
@@ -70,9 +70,8 @@
 }
 
 js_library("parent_access_ui_handler") {
-  deps = [
-    "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings_webui_js",
-  ]
+  deps =
+      [ "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js" ]
 }
 
 js_library("webview_manager") {
diff --git a/chrome/browser/resources/chromeos/parent_access/OWNERS b/chrome/browser/resources/chromeos/parent_access/OWNERS
index eb1b58bc..43ddb31 100644
--- a/chrome/browser/resources/chromeos/parent_access/OWNERS
+++ b/chrome/browser/resources/chromeos/parent_access/OWNERS
@@ -1 +1 @@
-file://chrome/browser/ui/webui/chromeos/parent_access/OWNERS
+file://chrome/browser/ui/webui/ash/parent_access/OWNERS
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn b/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
index ee56b3d..02100d7 100644
--- a/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
+++ b/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
@@ -8,7 +8,7 @@
 js_library("local_web_approvals_after") {
   deps = [
     "//ash/webui/common/resources:i18n_behavior",
-    "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings_webui_js",
+    "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.cc b/chrome/browser/supervised_user/supervised_user_interstitial.cc
index a317e711..74f0e0bb 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.cc
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.cc
@@ -63,31 +63,6 @@
 
 namespace {
 
-// For use in histograms.
-//
-// The enum values should remain synchronized with the enum
-// ManagedModeBlockingCommand in tools/metrics/histograms/enums.xml.
-//
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class Commands {
-  // PREVIEW = 0,
-  BACK = 1,
-  // NTP = 2,
-  REMOTE_ACCESS_REQUEST = 3,
-  LOCAL_ACCESS_REQUEST = 4,
-  HISTOGRAM_BOUNDING_VALUE = 5
-};
-
-// For use in histograms.The enum values should remain synchronized with the
-// enum ManagedUserURLRequestPermissionSource in
-// tools/metrics/histograms/enums.xml.
-enum class RequestPermissionSource {
-  MAIN_FRAME = 0,
-  SUB_FRAME,
-  HISTOGRAM_BOUNDING_VALUE
-};
-
 class TabCloser : public content::WebContentsUserData<TabCloser> {
  public:
   TabCloser(const TabCloser&) = delete;
@@ -263,15 +238,15 @@
   DCHECK_EQ(web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
             frame_id());
 
-  UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand",
-                            Commands::BACK, Commands::HISTOGRAM_BOUNDING_VALUE);
+  UMA_HISTOGRAM_ENUMERATION(kInterstitialCommandHistogramName, Commands::BACK,
+                            Commands::HISTOGRAM_BOUNDING_VALUE);
   AttemptMoveAwayFromCurrentFrameURL();
   OnInterstitialDone();
 }
 
 void SupervisedUserInterstitial::RequestUrlAccessRemote(
     base::OnceCallback<void(bool)> callback) {
-  UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand",
+  UMA_HISTOGRAM_ENUMERATION(kInterstitialCommandHistogramName,
                             Commands::REMOTE_ACCESS_REQUEST,
                             Commands::HISTOGRAM_BOUNDING_VALUE);
   OutputRequestPermissionSourceMetric();
@@ -284,7 +259,7 @@
 
 void SupervisedUserInterstitial::RequestUrlAccessLocal(
     base::OnceCallback<void(bool)> callback) {
-  UMA_HISTOGRAM_ENUMERATION("ManagedMode.BlockingInterstitialCommand",
+  UMA_HISTOGRAM_ENUMERATION(kInterstitialCommandHistogramName,
                             Commands::LOCAL_ACCESS_REQUEST,
                             Commands::HISTOGRAM_BOUNDING_VALUE);
   OutputRequestPermissionSourceMetric();
@@ -355,6 +330,6 @@
   else
     source = RequestPermissionSource::SUB_FRAME;
 
-  UMA_HISTOGRAM_ENUMERATION("ManagedUsers.RequestPermissionSource", source,
+  UMA_HISTOGRAM_ENUMERATION(kInterstitialPermissionSourceHistogramName, source,
                             RequestPermissionSource::HISTOGRAM_BOUNDING_VALUE);
 }
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.h b/chrome/browser/supervised_user/supervised_user_interstitial.h
index 30d98d92..158d12be 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.h
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.h
@@ -31,6 +31,42 @@
 // search.
 class SupervisedUserInterstitial {
  public:
+  // The names of histograms emitted by this class.
+  static constexpr char kInterstitialCommandHistogramName[] =
+      "ManagedMode.BlockingInterstitialCommand";
+  static constexpr char kInterstitialPermissionSourceHistogramName[] =
+      "ManagedUsers.RequestPermissionSource";
+
+  // For use in the kInterstitialCommandHistogramName histogram.
+  //
+  // The enum values should remain synchronized with the enum
+  // ManagedModeBlockingCommand in tools/metrics/histograms/enums.xml.
+  //
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class Commands {
+    // PREVIEW = 0,
+    BACK = 1,
+    // NTP = 2,
+    REMOTE_ACCESS_REQUEST = 3,
+    LOCAL_ACCESS_REQUEST = 4,
+    HISTOGRAM_BOUNDING_VALUE = 5
+  };
+
+  // For use in the kInterstitialPermissionSourceHistogramName histogram.
+  //
+  // The enum values should remain synchronized with the
+  // enum ManagedUserURLRequestPermissionSource in
+  // tools/metrics/histograms/enums.xml.
+  //
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class RequestPermissionSource {
+    MAIN_FRAME = 0,
+    SUB_FRAME,
+    HISTOGRAM_BOUNDING_VALUE
+  };
+
   SupervisedUserInterstitial(const SupervisedUserInterstitial&) = delete;
   SupervisedUserInterstitial& operator=(const SupervisedUserInterstitial&) =
       delete;
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
index bdf926e2..0ffb3d6 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
@@ -51,7 +51,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/shell.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/test/event_generator.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -293,7 +293,6 @@
 #endif
 IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
                        MAYBE_DisallowPrerendering) {
-  base::HistogramTester histogram_tester;
   const GURL initial_url = embedded_test_server()->GetURL("/simple.html");
   const GURL allowed_url =
       embedded_test_server()->GetURL("/supervised_user/simple.html");
@@ -662,6 +661,8 @@
                         /* local_web_approvals_preferred */ false)));
 
 IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest, BlockSubFrame) {
+  base::HistogramTester histogram_tester;
+
   BlockHost(kIframeHost2);
   GURL allowed_url_with_iframes = embedded_test_server()->GetURL(
       kExampleHost, "/supervised_user/with_iframes.html");
@@ -689,6 +690,13 @@
                             permission_creator()->url_requests()[0]);
 
   EXPECT_FALSE(IsInterstitialBeingShownInFrame(blocked_frame_id));
+
+  histogram_tester.ExpectUniqueSample(
+      SupervisedUserInterstitial::kInterstitialCommandHistogramName,
+      SupervisedUserInterstitial::Commands::REMOTE_ACCESS_REQUEST, 1);
+  histogram_tester.ExpectUniqueSample(
+      SupervisedUserInterstitial::kInterstitialPermissionSourceHistogramName,
+      SupervisedUserInterstitial::RequestPermissionSource::SUB_FRAME, 1);
 }
 
 IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest, BlockMultipleSubFrames) {
@@ -1033,6 +1041,7 @@
 
 IN_PROC_BROWSER_TEST_P(ChromeOSLocalWebApprovalsTest,
                        StartLocalWebApprovalsFromMainFrame) {
+  base::HistogramTester histogram_tester;
   BlockHost(kExampleHost);
 
   GURL blocked_url = embedded_test_server()->GetURL(
@@ -1049,21 +1058,29 @@
 
   // Trigger local approval flow - native dialog should appear.
   SendCommandToFrame(kLocalUrlAccessCommand, blocked_frame);
-  EXPECT_TRUE(chromeos::ParentAccessDialog::GetInstance());
+  EXPECT_TRUE(ash::ParentAccessDialog::GetInstance());
 
   // Close the flow without approval - interstitial should be still shown.
   ui::test::EventGenerator generator(ash::Shell::Get()->GetPrimaryRootWindow());
   generator.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
 
-  EXPECT_FALSE(chromeos::ParentAccessDialog::GetInstance());
+  EXPECT_FALSE(ash::ParentAccessDialog::GetInstance());
   EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
   EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
   EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
   CheckPreferredApprovalButton(blocked_frame);
+
+  histogram_tester.ExpectUniqueSample(
+      SupervisedUserInterstitial::kInterstitialCommandHistogramName,
+      SupervisedUserInterstitial::Commands::LOCAL_ACCESS_REQUEST, 1);
+  histogram_tester.ExpectUniqueSample(
+      SupervisedUserInterstitial::kInterstitialPermissionSourceHistogramName,
+      SupervisedUserInterstitial::RequestPermissionSource::MAIN_FRAME, 1);
 }
 
 IN_PROC_BROWSER_TEST_P(ChromeOSLocalWebApprovalsTest,
                        StartLocalWebApprovalsFromIframe) {
+  base::HistogramTester histogram_tester;
   BlockHost(kIframeHost1);
 
   const GURL allowed_url_with_iframes = embedded_test_server()->GetURL(
@@ -1082,18 +1099,25 @@
 
   // Trigger local approval flow - native dialog should appear.
   SendCommandToFrame(kLocalUrlAccessCommand, blocked_frame);
-  EXPECT_TRUE(chromeos::ParentAccessDialog::GetInstance());
+  EXPECT_TRUE(ash::ParentAccessDialog::GetInstance());
 
   // Close the flow without approval - interstitial should be still shown.
   ui::test::EventGenerator generator(ash::Shell::Get()->GetPrimaryRootWindow());
   generator.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
 
-  EXPECT_FALSE(chromeos::ParentAccessDialog::GetInstance());
+  EXPECT_FALSE(ash::ParentAccessDialog::GetInstance());
   EXPECT_FALSE(IsInterstitialBeingShownInMainFrame(browser()));
   EXPECT_TRUE(IsInterstitialBeingShownInFrame(blocked_frame));
   EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
   EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
   CheckPreferredApprovalButton(blocked_frame);
+
+  histogram_tester.ExpectUniqueSample(
+      SupervisedUserInterstitial::kInterstitialCommandHistogramName,
+      SupervisedUserInterstitial::Commands::LOCAL_ACCESS_REQUEST, 1);
+  histogram_tester.ExpectUniqueSample(
+      SupervisedUserInterstitial::kInterstitialPermissionSourceHistogramName,
+      SupervisedUserInterstitial::RequestPermissionSource::SUB_FRAME, 1);
 }
 
 IN_PROC_BROWSER_TEST_P(ChromeOSLocalWebApprovalsTest,
diff --git a/chrome/browser/supervised_user/web_approvals_manager.cc b/chrome/browser/supervised_user/web_approvals_manager.cc
index 0ebecbd..2370073 100644
--- a/chrome/browser/supervised_user/web_approvals_manager.cc
+++ b/chrome/browser/supervised_user/web_approvals_manager.cc
@@ -25,8 +25,8 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #endif
 
 namespace {
@@ -87,14 +87,13 @@
               parent_access_ui::mojom::WebApprovalsParams::New(
                   url.GetWithEmptyPath(), child_display_name, favicon_bytes)));
 
-  chromeos::ParentAccessDialogProvider provider;
-  chromeos::ParentAccessDialogProvider::ShowError result = provider.Show(
+  ash::ParentAccessDialogProvider provider;
+  ash::ParentAccessDialogProvider::ShowError result = provider.Show(
       std::move(params),
-      base::BindOnce(
-          [](std::unique_ptr<chromeos::ParentAccessDialog::Result> result)
-              -> void {}));
+      base::BindOnce([](std::unique_ptr<ash::ParentAccessDialog::Result> result)
+                         -> void {}));
 
-  if (result != chromeos::ParentAccessDialogProvider::ShowError::kNone) {
+  if (result != ash::ParentAccessDialogProvider::ShowError::kNone) {
     LOG(ERROR) << "Error showing ParentAccessDialog: " << result;
     std::move(callback).Run(false);
     return;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index be076b1..20845555 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2688,6 +2688,12 @@
       "webui/ash/multidevice_setup/multidevice_setup_handler.h",
       "webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc",
       "webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.h",
+      "webui/ash/parent_access/parent_access_dialog.cc",
+      "webui/ash/parent_access/parent_access_dialog.h",
+      "webui/ash/parent_access/parent_access_ui.cc",
+      "webui/ash/parent_access/parent_access_ui.h",
+      "webui/ash/parent_access/parent_access_ui_handler_impl.cc",
+      "webui/ash/parent_access/parent_access_ui_handler_impl.h",
       "webui/chromeos/assistant_optin/assistant_optin_ui.cc",
       "webui/chromeos/assistant_optin/assistant_optin_ui.h",
       "webui/chromeos/assistant_optin/assistant_optin_utils.cc",
@@ -2886,12 +2892,6 @@
       "webui/chromeos/notification_tester/notification_tester_ui.h",
       "webui/chromeos/onc_import_message_handler.cc",
       "webui/chromeos/onc_import_message_handler.h",
-      "webui/chromeos/parent_access/parent_access_dialog.cc",
-      "webui/chromeos/parent_access/parent_access_dialog.h",
-      "webui/chromeos/parent_access/parent_access_ui.cc",
-      "webui/chromeos/parent_access/parent_access_ui.h",
-      "webui/chromeos/parent_access/parent_access_ui_handler_impl.cc",
-      "webui/chromeos/parent_access/parent_access_ui_handler_impl.h",
       "webui/chromeos/power_ui.cc",
       "webui/chromeos/power_ui.h",
       "webui/chromeos/set_time_ui.cc",
@@ -3199,9 +3199,9 @@
       "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings",
       "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
       "//chrome/browser/ui/webui/ash/launcher_internals:mojo_bindings",
+      "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
+      "//chrome/browser/ui/webui/ash/parent_access:proto",
       "//chrome/browser/ui/webui/chromeos/manage_mirrorsync:mojo_bindings",
-      "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings",
-      "//chrome/browser/ui/webui/chromeos/parent_access:proto",
       "//chrome/browser/ui/webui/chromeos/vm:mojo_bindings",
       "//chrome/browser/ui/webui/nearby_share:mojom",
       "//chrome/browser/ui/webui/nearby_share/public/mojom",
@@ -4503,8 +4503,8 @@
       "views/extensions/reload_page_dialog.cc",
       "views/extensions/site_settings_expand_button.cc",
       "views/extensions/site_settings_expand_button.h",
-      "views/file_system_access/file_system_access_dangerous_file_dialog_view.cc",
-      "views/file_system_access/file_system_access_dangerous_file_dialog_view.h",
+      "views/file_system_access/file_system_access_dangerous_file_dialog.cc",
+      "views/file_system_access/file_system_access_dangerous_file_dialog.h",
       "views/file_system_access/file_system_access_icon_view.cc",
       "views/file_system_access/file_system_access_icon_view.h",
       "views/file_system_access/file_system_access_permission_view.cc",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 2e1dd282..72d08669 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -4166,8 +4166,8 @@
       <message name="IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_MESSAGE" desc="The snackbar message after a successful Web Feed follow request.">
         Following <ph name="SITE_NAME">%1$s<ex>Reuters</ex></ph>
       </message>
-      <message name="IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION" desc="The snackbar action after a successful Web Feed follow request.">
-        Go to feed
+      <message name="IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION_GO_TO_FOLLOWING" desc="The snackbar action after a successful Web Feed follow request, that will take the user to the Following feed.">
+        Go to Following
       </message>
       <message name="IDS_WEB_FEED_FOLLOW_GENERIC_FAILURE_SNACKBAR_MESSAGE" desc="The generic snackbar message after an unsuccessful Web Feed follow request.">
         Can’t follow. Something went wrong.
@@ -4196,8 +4196,11 @@
       <message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_STORIES_NOT_READY_DESCRIPTION" desc="The body text of the dialog presented a few times after a successful Web Feed follow request, when content from that Web Feed is not readily available. Please use the branded term for Discover, as listed under Product Names in the Google Glossary Manager (TC ID 1799975766543019278).">
         Soon, you’ll see stories from <ph name="SITE_NAME">%1$s<ex>Reuters</ex></ph> when you open a new tab. Sites you follow are saved in your Google account. You can manage them in Discover settings.
       </message>
-      <message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_OPEN_A_NEW_TAB" desc="The main action of the dialog presented a few times after a successful Web Feed follow request, when content from from that Web Feed is immediately available.">
-        Open a new tab
+      <message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_GO_TO_FOLLOWING" desc="The main action of the dialog presented a few times after a successful Web Feed follow request, when content from from that Web Feed is immediately available. It will take the user to the Following feed.">
+        Go to Following
+      </message>
+      <message name="IDS_WEB_FEED_POST_FOLLOW_DIALOG_GOT_IT" desc="The main action of the dialog presented a few times after a successful Web Feed follow request, when content from from that Web Feed is immediately available. The user is already at the Following feed so it will do nothing.">
+        Got it
       </message>
 
       <!-- Storage Preference UI strings for clearing storage. -->
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION.png.sha1
deleted file mode 100644
index 1294d42..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-40ccb741a3d10bc6a88557846d8f529678fe4578
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION_GO_TO_FOLLOWING.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION_GO_TO_FOLLOWING.png.sha1
new file mode 100644
index 0000000..dcddae0
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_FOLLOW_SUCCESS_SNACKBAR_ACTION_GO_TO_FOLLOWING.png.sha1
@@ -0,0 +1 @@
+28fed2948c37ad50036e1e6f84d6f707fe5b9f1f
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_GOT_IT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_GOT_IT.png.sha1
new file mode 100644
index 0000000..55539a6
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_GOT_IT.png.sha1
@@ -0,0 +1 @@
+151dd570f089062e87e4dad9c6cb761c756e07d1
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_GO_TO_FOLLOWING.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_GO_TO_FOLLOWING.png.sha1
new file mode 100644
index 0000000..baef5765
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_GO_TO_FOLLOWING.png.sha1
@@ -0,0 +1 @@
+87fbb80869208fb3f0028fb31af452528e847db7
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_OPEN_A_NEW_TAB.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_OPEN_A_NEW_TAB.png.sha1
deleted file mode 100644
index 4b1bcf4..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_WEB_FEED_POST_FOLLOW_DIALOG_OPEN_A_NEW_TAB.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c5b422a780eb5e56f1755b4af248e5f08328451a
\ No newline at end of file
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.cc b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.cc
new file mode 100644
index 0000000..15c35e9b
--- /dev/null
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.cc
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.h"
+
+#include "base/functional/callback_helpers.h"
+#include "chrome/browser/ui/views/file_system_access/file_system_access_ui_helpers.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/url_formatter/elide_url.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/dialog_model.h"
+#include "ui/base/models/dialog_model_field.h"
+
+namespace {
+
+using DangerousFileResult =
+    content::FileSystemAccessPermissionContext::SensitiveEntryResult;
+
+std::unique_ptr<ui::DialogModel> CreateFileSystemAccessDangerousFileDialog(
+    const url::Origin& origin,
+    const base::FilePath& path,
+    base::OnceCallback<
+        void(content::FileSystemAccessPermissionContext::SensitiveEntryResult)>
+        callback) {
+  auto split_callback = base::SplitOnceCallback(std::move(callback));
+  auto accept_callback = base::BindOnce(std::move(split_callback.first),
+                                        DangerousFileResult::kAllowed);
+  // Further split the cancel callback, which we need to pass to two different
+  // builder methods.
+  auto cancel_callbacks = base::SplitOnceCallback(base::BindOnce(
+      std::move(split_callback.second), DangerousFileResult::kAbort));
+
+  std::u16string formatted_origin =
+      url_formatter::FormatOriginForSecurityDisplay(
+          origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
+
+  ui::DialogModel::Builder dialog_builder;
+  dialog_builder
+      .SetTitle(l10n_util::GetStringFUTF16(
+          IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_TITLE,
+          file_system_access_ui_helper::GetPathForDisplay(path)))
+      .AddParagraph(ui::DialogModelLabel::CreateWithReplacement(
+          IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_TEXT,
+          ui::DialogModelLabel::CreateEmphasizedText(formatted_origin)))
+      .AddOkButton(
+          std::move(accept_callback),
+          l10n_util::GetStringUTF16(IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_SAVE))
+      .AddCancelButton(std::move(cancel_callbacks.first),
+                       l10n_util::GetStringUTF16(
+                           IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DONT_SAVE))
+      .SetCloseActionCallback(std::move(cancel_callbacks.second))
+      .OverrideDefaultButton(ui::DialogButton::DIALOG_BUTTON_CANCEL);
+  return dialog_builder.Build();
+}
+
+}  // namespace
+
+void ShowFileSystemAccessDangerousFileDialog(
+    const url::Origin& origin,
+    const base::FilePath& path,
+    base::OnceCallback<
+        void(content::FileSystemAccessPermissionContext::SensitiveEntryResult)>
+        callback,
+    content::WebContents* web_contents) {
+  constrained_window::ShowWebModal(CreateFileSystemAccessDangerousFileDialog(
+                                       origin, path, std::move(callback)),
+                                   web_contents);
+}
+
+std::unique_ptr<ui::DialogModel>
+CreateFileSystemAccessDangerousFileDialogForTesting(  // IN-TEST
+    const url::Origin& origin,
+    const base::FilePath& path,
+    base::OnceCallback<
+        void(content::FileSystemAccessPermissionContext::SensitiveEntryResult)>
+        callback) {
+  return CreateFileSystemAccessDangerousFileDialog(origin, path,
+                                                   std::move(callback));
+}
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.h b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.h
new file mode 100644
index 0000000..8601a7ed
--- /dev/null
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.h
@@ -0,0 +1,46 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DIALOG_H_
+#define CHROME_BROWSER_UI_VIEWS_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DIALOG_H_
+
+#include "content/public/browser/file_system_access_permission_context.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace ui {
+class DialogModel;
+}  // namespace ui
+
+namespace url {
+class Origin;
+}  // namespace url
+
+// A dialog that asks the user whether they want to save a file with a dangerous
+// extension. `callback` is called when the dialog is dismissed.
+// TODO(https://crbug.com/1352338): Consider moving this out of ui/views since
+// this no longer uses views code.
+void ShowFileSystemAccessDangerousFileDialog(
+    const url::Origin& origin,
+    const base::FilePath& path,
+    base::OnceCallback<
+        void(content::FileSystemAccessPermissionContext::SensitiveEntryResult)>
+        callback,
+    content::WebContents* web_contents);
+
+std::unique_ptr<ui::DialogModel>
+CreateFileSystemAccessDangerousFileDialogForTesting(
+    const url::Origin& origin,
+    const base::FilePath& path,
+    base::OnceCallback<
+        void(content::FileSystemAccessPermissionContext::SensitiveEntryResult)>
+        callback);
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DIALOG_H_
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_browsertest.cc b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_browsertest.cc
new file mode 100644
index 0000000..4ab385b
--- /dev/null
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_browsertest.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.h"
+
+#include "base/files/file_path.h"
+#include "base/functional/callback_helpers.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "content/public/test/browser_test.h"
+
+class FileSystemAccessDangerousFileDialogBrowserTest
+    : public DialogBrowserTest {
+ public:
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    ShowFileSystemAccessDangerousFileDialog(
+        url::Origin::Create(GURL("https://example.com")),
+        base::FilePath(FILE_PATH_LITERAL("bar.swf")), base::DoNothing(),
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(FileSystemAccessDangerousFileDialogBrowserTest,
+                       InvokeUi_default) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_unittest.cc b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_unittest.cc
new file mode 100644
index 0000000..f9e7a58
--- /dev/null
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog.h"
+
+#include "base/test/bind.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "content/public/browser/file_system_access_permission_context.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/test/test_dialog_model_host.h"
+#include "url/origin.h"
+
+using DangerousFileResult =
+    content::FileSystemAccessPermissionContext::SensitiveEntryResult;
+using FileSystemAccessDangerousFileDialogTest = BrowserWithTestWindowTest;
+
+namespace {
+
+class TestFileSystemAccessDangerousFileDialog {
+ public:
+  std::unique_ptr<ui::TestDialogModelHost> CreateDialogModelHost() {
+    return std::make_unique<ui::TestDialogModelHost>(
+        CreateFileSystemAccessDangerousFileDialogForTesting(
+            kTestOrigin, kTestPath,
+            base::BindLambdaForTesting([&](DangerousFileResult result) {
+              callback_called_ = true;
+              result_ = result;
+            })));
+  }
+
+  bool CallbackWasCalled() const { return callback_called_; }
+  DangerousFileResult Result() const {
+    CHECK(result_.has_value());
+    return result_.value();
+  }
+
+ private:
+  const url::Origin kTestOrigin =
+      url::Origin::Create(GURL("https://example.com"));
+  const base::FilePath kTestPath = base::FilePath(FILE_PATH_LITERAL("bar.swf"));
+
+  bool callback_called_ = false;
+  absl::optional<DangerousFileResult> result_ = absl::nullopt;
+};
+
+TEST_F(FileSystemAccessDangerousFileDialogTest, Accept) {
+  TestFileSystemAccessDangerousFileDialog test_dialog;
+  auto host = test_dialog.CreateDialogModelHost();
+
+  ui::TestDialogModelHost::Accept(std::move(host));
+
+  EXPECT_TRUE(test_dialog.CallbackWasCalled());
+  EXPECT_EQ(test_dialog.Result(), DangerousFileResult::kAllowed);
+}
+
+TEST_F(FileSystemAccessDangerousFileDialogTest, Cancel) {
+  TestFileSystemAccessDangerousFileDialog test_dialog;
+  auto host = test_dialog.CreateDialogModelHost();
+
+  ui::TestDialogModelHost::Cancel(std::move(host));
+
+  EXPECT_TRUE(test_dialog.CallbackWasCalled());
+  EXPECT_EQ(test_dialog.Result(), DangerousFileResult::kAbort);
+}
+
+TEST_F(FileSystemAccessDangerousFileDialogTest, Close) {
+  TestFileSystemAccessDangerousFileDialog test_dialog;
+  auto host = test_dialog.CreateDialogModelHost();
+
+  ui::TestDialogModelHost::Close(std::move(host));
+
+  EXPECT_TRUE(test_dialog.CallbackWasCalled());
+  EXPECT_EQ(test_dialog.Result(), DangerousFileResult::kAbort);
+}
+
+TEST_F(FileSystemAccessDangerousFileDialogTest, DestroyWithoutAction) {
+  TestFileSystemAccessDangerousFileDialog test_dialog;
+  auto host = test_dialog.CreateDialogModelHost();
+
+  ui::TestDialogModelHost::DestroyWithoutAction(std::move(host));
+
+  EXPECT_FALSE(test_dialog.CallbackWasCalled());
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.cc b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.cc
deleted file mode 100644
index fed65697..0000000
--- a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.h"
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/file_system_access/file_system_access_ui_helpers.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/constrained_window/constrained_window_views.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/ui_base_types.h"
-#include "ui/strings/grit/ui_strings.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/layout/fill_layout.h"
-
-FileSystemAccessDangerousFileDialogView::
-    ~FileSystemAccessDangerousFileDialogView() {
-  // Make sure the dialog ends up calling the callback no matter what.
-  if (callback_)
-    Close();
-  DCHECK(!callback_);
-}
-
-// static
-views::Widget* FileSystemAccessDangerousFileDialogView::ShowDialog(
-    const url::Origin& origin,
-    const base::FilePath& path,
-    base::OnceCallback<void(DangerousFileResult)> callback,
-    content::WebContents* web_contents) {
-  auto* browser = chrome::FindBrowserWithWebContents(web_contents);
-  auto delegate = base::WrapUnique(new FileSystemAccessDangerousFileDialogView(
-      browser, origin, path, std::move(callback)));
-  return constrained_window::ShowWebModalDialogViews(delegate.release(),
-                                                     web_contents);
-}
-
-FileSystemAccessDangerousFileDialogView::
-    FileSystemAccessDangerousFileDialogView(
-        Browser* browser,
-        const url::Origin& origin,
-        const base::FilePath& path,
-        base::OnceCallback<void(DangerousFileResult)> callback)
-    : callback_(std::move(callback)) {
-  SetTitle(l10n_util::GetStringFUTF16(
-      IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_TITLE,
-      file_system_access_ui_helper::GetPathForDisplay(path)));
-  SetButtonLabel(
-      ui::DIALOG_BUTTON_OK,
-      l10n_util::GetStringUTF16(IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_SAVE));
-  SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
-                 l10n_util::GetStringUTF16(
-                     IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DONT_SAVE));
-  // Ensure the default is to not save the dangerous file.
-  SetDefaultButton(ui::DIALOG_BUTTON_CANCEL);
-
-  auto run_callback = [](FileSystemAccessDangerousFileDialogView* dialog,
-                         DangerousFileResult result) {
-    std::move(dialog->callback_).Run(result);
-  };
-  SetAcceptCallback(base::BindOnce(run_callback, base::Unretained(this),
-                                   DangerousFileResult::kAllowed));
-  SetCancelCallback(base::BindOnce(run_callback, base::Unretained(this),
-                                   DangerousFileResult::kAbort));
-  SetCloseCallback(base::BindOnce(run_callback, base::Unretained(this),
-                                  DangerousFileResult::kAbort));
-
-  SetLayoutManager(std::make_unique<views::FillLayout>());
-  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
-      views::DialogContentType::kText, views::DialogContentType::kText));
-
-  SetModalType(ui::MODAL_TYPE_CHILD);
-  SetShowCloseButton(false);
-  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
-      views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
-
-  AddChildView(file_system_access_ui_helper::CreateOriginLabel(
-      browser, IDS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_TEXT, origin,
-      views::style::CONTEXT_DIALOG_BODY_TEXT, /*show_emphasis=*/true));
-}
-
-BEGIN_METADATA(FileSystemAccessDangerousFileDialogView,
-               views::DialogDelegateView)
-END_METADATA
-
-void ShowFileSystemAccessDangerousFileDialog(
-    const url::Origin& origin,
-    const base::FilePath& path,
-    base::OnceCallback<void(
-        FileSystemAccessDangerousFileDialogView::DangerousFileResult)> callback,
-    content::WebContents* web_contents) {
-  FileSystemAccessDangerousFileDialogView::ShowDialog(
-      origin, path, std::move(callback), web_contents);
-}
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.h b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.h
deleted file mode 100644
index 0759be594..0000000
--- a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DIALOG_VIEW_H_
-#define CHROME_BROWSER_UI_VIEWS_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DIALOG_VIEW_H_
-
-#include "chrome/browser/ui/browser.h"
-#include "content/public/browser/file_system_access_permission_context.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/views/window/dialog_delegate.h"
-
-namespace base {
-class FilePath;
-}
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-namespace url {
-class Origin;
-}  // namespace url
-
-namespace views {
-class Widget;
-}  // namespace views
-
-// A dialog that asks the user whether they want to save a file with a dangerous
-// extension.
-class FileSystemAccessDangerousFileDialogView
-    : public views::DialogDelegateView {
- public:
-  METADATA_HEADER(FileSystemAccessDangerousFileDialogView);
-
-  using DangerousFileResult =
-      content::FileSystemAccessPermissionContext::SensitiveEntryResult;
-
-  FileSystemAccessDangerousFileDialogView(
-      const FileSystemAccessDangerousFileDialogView&) = delete;
-  FileSystemAccessDangerousFileDialogView& operator=(
-      const FileSystemAccessDangerousFileDialogView&) = delete;
-  ~FileSystemAccessDangerousFileDialogView() override;
-
-  // Creates and shows the dialog. `callback` is called when the dialog is
-  // dismissed.
-  static views::Widget* ShowDialog(
-      const url::Origin& origin,
-      const base::FilePath& path,
-      base::OnceCallback<void(DangerousFileResult)> callback,
-      content::WebContents* web_contents);
-
- private:
-  FileSystemAccessDangerousFileDialogView(
-      Browser* browser,
-      const url::Origin& origin,
-      const base::FilePath& path,
-      base::OnceCallback<void(DangerousFileResult)> callback);
-
-  base::OnceCallback<void(DangerousFileResult)> callback_;
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_DANGEROUS_FILE_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view_browsertest.cc b/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view_browsertest.cc
deleted file mode 100644
index 6ad356243..0000000
--- a/chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view_browsertest.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view.h"
-
-#include "base/files/file_path.h"
-#include "base/memory/raw_ptr.h"
-#include "base/test/bind.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/test/test_browser_dialog.h"
-#include "chrome/grit/generated_resources.h"
-#include "content/public/test/browser_test.h"
-#include "ui/base/resource/resource_bundle.h"
-
-using SensitiveEntryResult =
-    content::FileSystemAccessPermissionContext::SensitiveEntryResult;
-
-class FileSystemAccessDangerousFileDialogViewTest : public DialogBrowserTest {
- public:
-  // DialogBrowserTest:
-  void ShowUi(const std::string& name) override {
-    widget_ = FileSystemAccessDangerousFileDialogView::ShowDialog(
-        kTestOrigin, base::FilePath(FILE_PATH_LITERAL("bar.swf")),
-        base::BindLambdaForTesting([&](SensitiveEntryResult result) {
-          callback_called_ = true;
-          callback_result_ = result;
-        }),
-        browser()->tab_strip_model()->GetActiveWebContents());
-  }
-
- protected:
-  const url::Origin kTestOrigin =
-      url::Origin::Create(GURL("https://example.com"));
-
-  raw_ptr<views::Widget> widget_ = nullptr;
-
-  bool callback_called_ = false;
-  SensitiveEntryResult callback_result_ = SensitiveEntryResult::kAllowed;
-};
-
-IN_PROC_BROWSER_TEST_F(FileSystemAccessDangerousFileDialogViewTest,
-                       AcceptRunsCallback) {
-  ShowUi(std::string());
-  widget_->widget_delegate()->AsDialogDelegate()->AcceptDialog();
-  EXPECT_TRUE(callback_called_);
-  EXPECT_EQ(SensitiveEntryResult::kAllowed, callback_result_);
-  base::RunLoop().RunUntilIdle();
-}
-
-IN_PROC_BROWSER_TEST_F(FileSystemAccessDangerousFileDialogViewTest,
-                       CancelRunsCallback) {
-  ShowUi(std::string());
-  widget_->widget_delegate()->AsDialogDelegate()->CancelDialog();
-  EXPECT_TRUE(callback_called_);
-  EXPECT_EQ(SensitiveEntryResult::kAbort, callback_result_);
-  base::RunLoop().RunUntilIdle();
-}
-
-IN_PROC_BROWSER_TEST_F(FileSystemAccessDangerousFileDialogViewTest,
-                       InvokeUi_default) {
-  ShowAndVerifyUi();
-}
diff --git a/chrome/browser/ui/webui/app_home/app_home_page_handler_unittest.cc b/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
similarity index 80%
rename from chrome/browser/ui/webui/app_home/app_home_page_handler_unittest.cc
rename to chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
index 3703f14a..56491d12 100644
--- a/chrome/browser/ui/webui/app_home/app_home_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
@@ -9,16 +9,17 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/app_home/app_home.mojom.h"
 #include "chrome/browser/ui/webui/app_home/mock_app_home_page.h"
-#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
-#include "chrome/browser/web_applications/test/web_app_test.h"
+#include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
 #include "content/public/test/test_web_ui.h"
+#include "extensions/browser/extension_dialog_auto_confirm.h"
 #include "extensions/common/extension_builder.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
 
 using web_app::AppId;
 using GetAppsCallback =
@@ -108,7 +109,7 @@
 
 }  // namespace
 
-class AppHomePageHandlerTest : public WebAppTest {
+class AppHomePageHandlerTest : public InProcessBrowserTest {
  public:
   AppHomePageHandlerTest() = default;
 
@@ -117,23 +118,19 @@
 
   ~AppHomePageHandlerTest() override = default;
 
-  void SetUp() override {
-    WebAppTest::SetUp();
+ protected:
+  std::unique_ptr<TestAppHomePageHandler> GetAppHomePageHandler() {
+    AddBlankTabAndShow(browser());
+    content::WebContents* contents =
+        browser()->tab_strip_model()->GetWebContentsAt(0);
+    test_web_ui_.set_web_contents(contents);
 
-    web_app::FakeWebAppProvider* provider =
-        web_app::FakeWebAppProvider::Get(profile());
-    provider->SetDefaultFakeSubsystems();
-
-    extension_service_ = CreateTestExtensionService();
-
-    web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
+    return std::make_unique<TestAppHomePageHandler>(&test_web_ui_, profile(),
+                                                    page_.BindAndGetRemote());
   }
 
- protected:
-  std::unique_ptr<TestAppHomePageHandler> GetAppHomePageHandler(
-      content::TestWebUI* test_web_ui) {
-    return std::make_unique<TestAppHomePageHandler>(test_web_ui, profile(),
-                                                    page_.BindAndGetRemote());
+  extensions::ExtensionService* extension_service() {
+    return extensions::ExtensionSystem::Get(profile())->extension_service();
   }
 
   AppId InstallTestWebApp() {
@@ -143,6 +140,8 @@
     return installed_app_id;
   }
 
+  Profile* profile() { return browser()->profile(); }
+
   void UninstallTestWebApp(const web_app::AppId& app_id) {
     web_app::test::UninstallWebApp(profile(), app_id);
   }
@@ -150,7 +149,7 @@
   scoped_refptr<const extensions::Extension> InstallTestExtensionApp() {
     scoped_refptr<const extensions::Extension> extension =
         extensions::ExtensionBuilder(kTestAppName).Build();
-    extension_service_->AddExtension(extension.get());
+    extension_service()->AddExtension(extension.get());
     return extension;
   }
 
@@ -168,7 +167,7 @@
     // locking semantics on WinOS platfom. To workaround this case, make sure
     // the task of uninstalling extension complete before the `AppHome` test
     // tear down.
-    extension_service_->UninstallExtension(
+    extension_service()->UninstallExtension(
         extension->id(),
         extensions::UninstallReason::UNINSTALL_REASON_FOR_TESTING, &error,
         base::BindOnce(
@@ -179,12 +178,6 @@
     run_loop.Run();
   }
 
-  std::unique_ptr<content::TestWebUI> CreateTestWebUI() {
-    auto test_web_ui = std::make_unique<content::TestWebUI>();
-    test_web_ui->set_web_contents(web_contents());
-    return test_web_ui;
-  }
-
   extensions::ExtensionService* CreateTestExtensionService() {
     auto* extension_system = static_cast<extensions::TestExtensionSystem*>(
         extensions::ExtensionSystem::Get(profile()));
@@ -195,8 +188,8 @@
     return ext_service;
   }
 
+  content::TestWebUI test_web_ui_;
   testing::StrictMock<MockAppHomePage> page_;
-  raw_ptr<extensions::ExtensionService> extension_service_;
 };
 
 MATCHER_P(MatchAppName, expected_app_name, "") {
@@ -213,12 +206,11 @@
   return false;
 }
 
-TEST_F(AppHomePageHandlerTest, GetApps) {
+IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, GetApps) {
   AppId installed_app_id = InstallTestWebApp();
 
-  std::unique_ptr<content::TestWebUI> test_web_ui = CreateTestWebUI();
   std::unique_ptr<TestAppHomePageHandler> page_handler =
-      GetAppHomePageHandler(test_web_ui.get());
+      GetAppHomePageHandler();
 
   std::vector<app_home::mojom::AppInfoPtr> app_infos;
   base::RunLoop run_loop;
@@ -226,26 +218,21 @@
       WrapGetAppsCallback(&app_infos, run_loop.QuitClosure()));
   run_loop.Run();
 
-  ASSERT_EQ(1u, app_infos.size());
   EXPECT_EQ(kTestAppUrl, app_infos[0]->start_url);
   EXPECT_EQ(kTestAppName, app_infos[0]->name);
 }
 
-TEST_F(AppHomePageHandlerTest, OnWebAppInstalled) {
-  std::unique_ptr<content::TestWebUI> test_web_ui = CreateTestWebUI();
+IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, OnWebAppInstalled) {
   std::unique_ptr<TestAppHomePageHandler> page_handler =
-      GetAppHomePageHandler(test_web_ui.get());
-
+      GetAppHomePageHandler();
   EXPECT_CALL(page_, AddApp(MatchAppName(kTestAppName)));
   AppId installed_app_id = InstallTestWebApp();
   page_handler->Wait();
 }
 
-TEST_F(AppHomePageHandlerTest, OnExtensionLoaded) {
-  std::unique_ptr<content::TestWebUI> test_web_ui = CreateTestWebUI();
+IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, OnExtensionLoaded) {
   std::unique_ptr<TestAppHomePageHandler> page_handler =
-      GetAppHomePageHandler(test_web_ui.get());
-
+      GetAppHomePageHandler();
   EXPECT_CALL(page_, AddApp(MatchAppName(kTestAppName)));
   scoped_refptr<const extensions::Extension> extension =
       InstallTestExtensionApp();
@@ -253,10 +240,9 @@
   page_handler->Wait();
 }
 
-TEST_F(AppHomePageHandlerTest, OnWebAppUninstall) {
-  std::unique_ptr<content::TestWebUI> test_web_ui = CreateTestWebUI();
+IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, OnWebAppUninstall) {
   std::unique_ptr<TestAppHomePageHandler> page_handler =
-      GetAppHomePageHandler(test_web_ui.get());
+      GetAppHomePageHandler();
 
   // First, install a web app for test.
   EXPECT_CALL(page_, AddApp(MatchAppName(kTestAppName)));
@@ -270,10 +256,9 @@
   page_handler->Wait();
 }
 
-TEST_F(AppHomePageHandlerTest, OnExtensionUninstall) {
-  std::unique_ptr<content::TestWebUI> test_web_ui = CreateTestWebUI();
+IN_PROC_BROWSER_TEST_F(AppHomePageHandlerTest, OnExtensionUninstall) {
   std::unique_ptr<TestAppHomePageHandler> page_handler =
-      GetAppHomePageHandler(test_web_ui.get());
+      GetAppHomePageHandler();
 
   // First, install a test extension app for test.
   EXPECT_CALL(page_, AddApp(MatchAppName(kTestAppName)));
diff --git a/chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_page_handler.cc b/chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_page_handler.cc
index 0589972..2b88c63 100644
--- a/chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_page_handler.cc
+++ b/chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_page_handler.cc
@@ -11,13 +11,13 @@
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/system/sys_info.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/crostini/crostini_disk.h"
 #include "chrome/browser/ash/crostini/crostini_installer_ui_delegate.h"
 #include "chrome/browser/ash/crostini/crostini_types.mojom.h"
 #include "chrome/browser/ash/crostini/crostini_util.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/text/bytes_formatting.h"
 
@@ -28,8 +28,13 @@
 void OnAmountOfFreeDiskSpace(
     crostini_installer::mojom::PageHandler::RequestAmountOfFreeDiskSpaceCallback
         callback,
-    int64_t free_bytes) {
-  int64_t max_bytes = free_bytes - crostini::disk::kDiskHeadroomBytes;
+    absl::optional<int64_t> free_bytes) {
+  if (!free_bytes.has_value()) {
+    std::move(callback).Run({}, 0, false);
+    return;
+  }
+
+  int64_t max_bytes = free_bytes.value() - crostini::disk::kDiskHeadroomBytes;
 
   if (max_bytes < crostini::disk::kMinimumDiskSizeBytes) {
     std::move(callback).Run({}, 0, false);
@@ -114,10 +119,8 @@
 
 void CrostiniInstallerPageHandler::RequestAmountOfFreeDiskSpace(
     RequestAmountOfFreeDiskSpaceCallback callback) {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
-                     base::FilePath(crostini::kHomeDirectory)),
+  ash::SpacedClient::Get()->GetFreeDiskSpace(
+      crostini::kHomeDirectory,
       base::BindOnce(OnAmountOfFreeDiskSpace, std::move(callback)));
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/BUILD.gn b/chrome/browser/ui/webui/ash/parent_access/BUILD.gn
similarity index 87%
rename from chrome/browser/ui/webui/chromeos/parent_access/BUILD.gn
rename to chrome/browser/ui/webui/ash/parent_access/BUILD.gn
index 32a7679..9e84eaf 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/parent_access/BUILD.gn
@@ -2,9 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//third_party/protobuf/proto_library.gni")
 
+assert(is_chromeos_ash)
+
 proto_library("proto") {
   sources = [ "parent_access_callback.proto" ]
 }
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/DEPS b/chrome/browser/ui/webui/ash/parent_access/DEPS
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/parent_access/DEPS
rename to chrome/browser/ui/webui/ash/parent_access/DEPS
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/DIR_METADATA b/chrome/browser/ui/webui/ash/parent_access/DIR_METADATA
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/parent_access/DIR_METADATA
rename to chrome/browser/ui/webui/ash/parent_access/DIR_METADATA
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/OWNERS b/chrome/browser/ui/webui/ash/parent_access/OWNERS
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/parent_access/OWNERS
rename to chrome/browser/ui/webui/ash/parent_access/OWNERS
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.cc
similarity index 90%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.cc
index df0bf2a..ec13397 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.cc
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h"
 
 #include <string>
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 
-namespace chromeos {
+namespace ash {
 
 ParentAccessBrowserTestBase::ParentAccessBrowserTestBase() = default;
 ParentAccessBrowserTestBase::~ParentAccessBrowserTestBase() = default;
@@ -32,7 +32,7 @@
 }
 
 ParentAccessUI* ParentAccessBrowserTestBase::GetParentAccessUI() {
-  return static_cast<chromeos::ParentAccessUI*>(GetWebUI()->GetController());
+  return static_cast<ParentAccessUI*>(GetWebUI()->GetController());
 }
 
 content::WebContents* ParentAccessBrowserTestBase::GetContents() {
@@ -82,4 +82,4 @@
   return LoggedInUserMixin::LogInType::kChild;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h b/chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h
similarity index 84%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h
index 336528a..2b8762144 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_BROWSERTEST_BASE_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_BROWSERTEST_BASE_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_BROWSERTEST_BASE_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_BROWSERTEST_BASE_H_
 
 #include <memory>
 
 #include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h"
 
 namespace content {
 class WebContents;
@@ -19,7 +19,7 @@
 class IdentityTestEnvironment;
 }
 
-namespace chromeos {
+namespace ash {
 
 // Base class for ParentAccessUI tests.
 class ParentAccessBrowserTestBase : public MixinBasedInProcessBrowserTest {
@@ -39,7 +39,7 @@
   // WebUI should not exist unless a dialog has been created. When writing
   // tests, create a dialog if the WebUI needs to be created.
   content::WebUI* GetWebUI();
-  chromeos::ParentAccessUI* GetParentAccessUI();
+  ParentAccessUI* GetParentAccessUI();
   content::WebContents* GetContents();
 
  protected:
@@ -85,6 +85,6 @@
   LoggedInUserMixin::LogInType GetLogInType() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_BROWSERTEST_BASE_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_BROWSERTEST_BASE_H_
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_callback.proto b/chrome/browser/ui/webui/ash/parent_access/parent_access_callback.proto
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_callback.proto
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_callback.proto
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc
similarity index 93%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc
index fa9c4c61..357b687 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 
 #include <utility>
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -97,4 +97,4 @@
               : std::make_unique<ParentAccessDialog::Result>());
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h b/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h
similarity index 88%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h
index 090f1545..4efeb65f 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
 
 #include <memory>
 #include <string>
 
 #include "base/callback_forward.h"
 #include "base/time/time.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
 
-namespace chromeos {
+namespace ash {
 
 // Dialog which embeds the Parent Access UI, which verifies a parent during
 // a child session.
@@ -81,11 +81,11 @@
 // is can be overridden by tests to provide a fake implementation like this:
 //
 // class FakeParentAccessDialogProvider
-//    : public chromeos::ParentAccessDialogProvider {
+//    : public ash::ParentAccessDialogProvider {
 // public:
 //  ParentAccessDialogProvider::ShowError Show(
 //      parent_access_ui::mojom::ParentAccessParamsPtr params,
-//      chromeos::ParentAccessDialog::Callback callback) override {}
+//      ash::ParentAccessDialog::Callback callback) override {}
 class ParentAccessDialogProvider {
  public:
   // Error state returned by the Show() function.
@@ -103,6 +103,6 @@
                          ParentAccessDialog::Callback callback);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc
similarity index 92%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc
index 8f5ad835..3ee7604 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include <memory>
 
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chrome/common/webui_url_constants.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
@@ -23,14 +23,14 @@
 #include "url/gurl.h"
 
 namespace {
-bool DialogResultsEqual(const chromeos::ParentAccessDialog::Result& first,
-                        const chromeos::ParentAccessDialog::Result& second) {
+bool DialogResultsEqual(const ash::ParentAccessDialog::Result& first,
+                        const ash::ParentAccessDialog::Result& second) {
   return first.status == second.status &&
          first.parent_access_token == second.parent_access_token;
 }
 }  // namespace
 
-namespace chromeos {
+namespace ash {
 
 using ParentAccessDialogBrowserTest = ParentAccessChildUserBrowserTestBase;
 
@@ -75,7 +75,7 @@
 
   // Send ESCAPE keypress.  EventGenerator requires the root window, which has
   // to be fetched from the Ash shell.
-  ui::test::EventGenerator generator(ash::Shell::Get()->GetPrimaryRootWindow());
+  ui::test::EventGenerator generator(Shell::Get()->GetPrimaryRootWindow());
   generator.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
 
   // The dialog instance should be gone after ESC is pressed.
@@ -180,4 +180,4 @@
 // TODO(b/241166361) Add test to ensure PAT is communicated back to caller via
 // the the ParentAccessDialog::Callback.
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
similarity index 96%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
index 8be562c..c1095683 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h"
 
 #include <string>
 #include <utility>
@@ -13,7 +13,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/browser_resources.h"
@@ -25,7 +25,7 @@
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "url/gurl.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -160,4 +160,4 @@
 
 WEB_UI_CONTROLLER_TYPE_IMPL(ParentAccessUI)
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h
similarity index 77%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h
index 0cdc07d..112318b4 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_UI_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_UI_H_
 
 #include <memory>
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom-forward.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom-forward.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/web_dialogs/web_dialog_ui.h"
 #include "url/gurl.h"
 
-namespace chromeos {
+namespace ash {
 
 // Controller for the ParentAccessUI, a WebUI which enables parent verification.
 // It is hosted at chrome://parent-access.
@@ -52,6 +52,6 @@
   WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_UI_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_browsertest.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_browsertest.cc
similarity index 91%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_browsertest.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui_browsertest.cc
index f1f64bc..961c82a 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_browsertest.cc
@@ -8,8 +8,8 @@
 #include "base/functional/callback_helpers.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/google/core/common/google_util.h"
@@ -17,7 +17,7 @@
 #include "content/public/test/browser_test.h"
 #include "url/gurl.h"
 
-namespace chromeos {
+namespace ash {
 
 using ParentAccessUIBrowserTest = ParentAccessChildUserBrowserTestBase;
 
@@ -61,4 +61,4 @@
       google_util::GetGoogleLocale(g_browser_process->GetApplicationLocale()));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc
similarity index 94%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc
index 38172ec6..c6482755 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h"
 
 #include <string>
 #include <utility>
@@ -11,9 +11,9 @@
 #include "base/notreached.h"
 #include "base/time/time.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_callback.pb.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_callback.pb.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/access_token_fetcher.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
@@ -22,7 +22,7 @@
 #include "google_apis/gaia/gaia_constants.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
-namespace chromeos {
+namespace ash {
 
 ParentAccessUIHandlerImpl::ParentAccessUIHandlerImpl(
     mojo::PendingReceiver<parent_access_ui::mojom::ParentAccessUIHandler>
@@ -175,4 +175,4 @@
   }
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.h b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h
similarity index 84%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.h
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h
index 887d5932..891b133 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.h
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_UI_HANDLER_IMPL_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_UI_HANDLER_IMPL_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_UI_HANDLER_IMPL_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_UI_HANDLER_IMPL_H_
 
 #include <memory>
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_callback.pb.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_callback.pb.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
@@ -25,7 +25,7 @@
 
 class GoogleServiceAuthError;
 
-namespace chromeos {
+namespace ash {
 
 class ParentAccessUIHandlerImpl
     : public parent_access_ui::mojom::ParentAccessUIHandler {
@@ -76,6 +76,6 @@
   base::WeakPtrFactory<ParentAccessUIHandlerImpl> weak_ptr_factory_{this};
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_UI_HANDLER_IMPL_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_PARENT_ACCESS_PARENT_ACCESS_UI_HANDLER_IMPL_H_
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl_browsertest.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl_browsertest.cc
similarity index 94%
rename from chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl_browsertest.cc
rename to chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl_browsertest.cc
index 5edebe6..2dcaa6d 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl_browsertest.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl.h"
+
 #include <memory>
 #include <string>
 
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl.h"
-
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_callback.pb.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_callback.pb.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
@@ -30,7 +30,7 @@
 }
 }  // namespace
 
-namespace chromeos {
+namespace ash {
 
 using ParentAccessUIHandlerImplBrowserTest =
     ParentAccessChildUserBrowserTestBase;
@@ -159,8 +159,7 @@
       GetParamsForWebApprovals(),
       base::BindOnce(
           [](base::OnceClosure quit_closure,
-             std::unique_ptr<chromeos::ParentAccessDialog::Result> result)
-              -> void {
+             std::unique_ptr<ParentAccessDialog::Result> result) -> void {
             // The dialog result should contain the test token and expire
             // timestamp.
             EXPECT_EQ("TEST_TOKEN", result->parent_access_token);
@@ -233,11 +232,9 @@
       GetParamsForWebApprovals(),
       base::BindOnce(
           [](base::OnceClosure quit_closure,
-             std::unique_ptr<chromeos::ParentAccessDialog::Result> result)
-              -> void {
+             std::unique_ptr<ParentAccessDialog::Result> result) -> void {
             // The dialog result should contain the test token.
-            EXPECT_EQ(chromeos::ParentAccessDialog::Result::kDeclined,
-                      result->status);
+            EXPECT_EQ(ParentAccessDialog::Result::kDeclined, result->status);
             std::move(quit_closure).Run();
           },
           show_dialog_run_loop.QuitClosure()));
@@ -393,4 +390,4 @@
           }));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index f3ca049..ada065b 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -266,6 +266,7 @@
 #include "chrome/browser/ui/webui/ash/launcher_internals/launcher_internals_ui.h"
 #include "chrome/browser/ui/webui/ash/multidevice_internals/multidevice_internals_ui.h"
 #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h"
+#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h"
@@ -278,7 +279,6 @@
 #include "chrome/browser/ui/webui/chromeos/manage_mirrorsync/manage_mirrorsync_ui.h"
 #include "chrome/browser/ui/webui/chromeos/network_ui.h"
 #include "chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h"
-#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.h"
 #include "chrome/browser/ui/webui/chromeos/power_ui.h"
 #include "chrome/browser/ui/webui/chromeos/set_time_ui.h"
 #include "chrome/browser/ui/webui/chromeos/slow_trace_ui.h"
@@ -992,7 +992,7 @@
   if (url.host_piece() == chrome::kChromeUIAddSupervisionHost)
     return &NewWebUI<ash::AddSupervisionUI>;
   if (url.host_piece() == chrome::kChromeUIParentAccessHost)
-    return &NewWebUI<chromeos::ParentAccessUI>;
+    return &NewWebUI<ash::ParentAccessUI>;
   if (url.host_piece() == chrome::kChromeUIAudioHost &&
       base::FeatureList::IsEnabled(chromeos::features::kAudioUrl)) {
     return &NewWebUI<ash::AudioUI>;
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 979c7128..769d574c 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1666029593-ced6dd87becdd1f0649e3a24f41821485b40b899.profdata
+chrome-linux-main-1666051031-9fef363a004acbe828e285c4ebe2958af25ff079.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 3b9aea60..ffa2e4f1 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1666029593-44512b2560803554918539a059d4bd5cc870211b.profdata
+chrome-mac-arm-main-1666051031-86a9ee63c516c44c7ffa465cc3a186f00244656c.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index cccf36be..0137180 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1666029593-e6ed3ce9ba2c388109f472f1f99c3c811b845568.profdata
+chrome-mac-main-1666051031-c2bd1f0c9fbb539243edc82eb4ad09ffcd83dbda.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 66f7c10b2..700f610 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1666018656-15324d08013017a89031e80a4c773579d86a24ff.profdata
+chrome-win32-main-1666051031-dfd2d9df4cc0073e32dfe95e9908f830c20433c0.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 8047da6..92ee06c 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1666029593-b92f5ea1668afad0cff79988d756ae03b4a725b7.profdata
+chrome-win64-main-1666051031-683191e93467efe360e282ff4ce44b6a80b0cd29.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 87090d5..74eba95 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3141,6 +3141,9 @@
           "../browser/extensions/api/system_indicator/system_indicator_apitest.cc",
           "../browser/extensions/api/web_authentication_proxy/web_authentication_proxy_apitest.cc",
           "../browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc",
+          "../browser/ui/webui/app_home/app_home_page_handler_browsertest.cc",
+          "../browser/ui/webui/app_home/mock_app_home_page.cc",
+          "../browser/ui/webui/app_home/mock_app_home_page.h",
         ]
       }
 
@@ -3161,6 +3164,7 @@
         deps += [
           "//chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands:test_support",
           "//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence:test_support",
+          "//chrome/browser/ui/webui/app_home:mojo_bindings",
         ]
       }
 
@@ -3362,7 +3366,7 @@
         "../browser/ui/views/extensions/settings_overridden_dialog_browsertest.cc",
         "../browser/ui/views/external_protocol_dialog_browsertest.cc",
         "../browser/ui/views/file_system_access/file_system_access_browsertest.cc",
-        "../browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_view_browsertest.cc",
+        "../browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_browsertest.cc",
         "../browser/ui/views/file_system_access/file_system_access_permission_view_browsertest.cc",
         "../browser/ui/views/file_system_access/file_system_access_restricted_directory_dialog_view_browsertest.cc",
         "../browser/ui/views/file_system_access/file_system_access_usage_bubble_view_browsertest.cc",
@@ -3593,6 +3597,8 @@
         "../browser/ash/accessibility/dictation_bubble_test_helper.h",
         "../browser/ash/accessibility/magnification_controller_browsertest.cc",
         "../browser/ash/accessibility/magnification_manager_browsertest.cc",
+        "../browser/ash/accessibility/magnifier_animation_waiter.cc",
+        "../browser/ash/accessibility/magnifier_animation_waiter.h",
         "../browser/ash/accessibility/select_to_speak_browsertest.cc",
         "../browser/ash/accessibility/service/accessibility_service_client_browsertest.cc",
         "../browser/ash/accessibility/spoken_feedback_app_list_browsertest.cc",
@@ -4074,16 +4080,16 @@
         "../browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc",
         "../browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc",
         "../browser/ui/webui/ash/crostini_upgrader/crostini_upgrader_dialog_browsertest.cc",
+        "../browser/ui/webui/ash/parent_access/parent_access_browsertest_base.cc",
+        "../browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h",
+        "../browser/ui/webui/ash/parent_access/parent_access_dialog_browsertest.cc",
+        "../browser/ui/webui/ash/parent_access/parent_access_ui_browsertest.cc",
+        "../browser/ui/webui/ash/parent_access/parent_access_ui_handler_impl_browsertest.cc",
         "../browser/ui/webui/chromeos/bluetooth_pairing_dialog_browsertest-inl.h",
         "../browser/ui/webui/chromeos/edu_coexistence/edu_coexistence_login_handler_browsertest.cc",
         "../browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc",
         "../browser/ui/webui/chromeos/login/testapi/oobe_test_api_browsertest.cc",
         "../browser/ui/webui/chromeos/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc",
-        "../browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.cc",
-        "../browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h",
-        "../browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc",
-        "../browser/ui/webui/chromeos/parent_access/parent_access_ui_browsertest.cc",
-        "../browser/ui/webui/chromeos/parent_access/parent_access_ui_handler_impl_browsertest.cc",
         "../browser/ui/webui/chromeos/smb_shares/smb_credentials_dialog_browsertest.cc",
         "../browser/ui/webui/chromeos/system_web_dialog_browsertest.cc",
         "../browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc",
@@ -4246,9 +4252,9 @@
         "//chrome/browser/ui/ash/system_web_apps",
         "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
         "//chrome/browser/ui/webui/ash/crostini_upgrader:mojo_bindings",
+        "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
+        "//chrome/browser/ui/webui/ash/parent_access:proto",
         "//chrome/browser/ui/webui/chromeos/manage_mirrorsync:mojo_bindings",
-        "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings",
-        "//chrome/browser/ui/webui/chromeos/parent_access:proto",
         "//chrome/browser/ui/webui/settings/ash:test_support",
         "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
         "//chrome/services/file_util/public/cpp:browser_tests",
@@ -6895,6 +6901,7 @@
       "../browser/ui/views/autofill_assistant/password_change/password_change_animated_icon_unittest.cc",
       "../browser/ui/views/autofill_assistant/password_change/password_change_run_progress_unittest.cc",
       "../browser/ui/views/autofill_assistant/password_change/password_change_run_view_unittest.cc",
+      "../browser/ui/views/file_system_access/file_system_access_dangerous_file_dialog_unittest.cc",
       "../browser/ui/views/file_system_access/file_system_access_ui_helpers_unittest.cc",
       "../browser/ui/views/passwords/move_to_account_store_bubble_view_unittest.cc",
       "../browser/ui/views/passwords/password_bubble_view_test_base.cc",
@@ -8541,9 +8548,6 @@
       "../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",
-      "../browser/ui/webui/app_home/app_home_page_handler_unittest.cc",
-      "../browser/ui/webui/app_home/mock_app_home_page.cc",
-      "../browser/ui/webui/app_home/mock_app_home_page.h",
       "../browser/ui/webui/ntp/app_launcher_handler_unittest.cc",
       "../browser/ui/webui/whats_new/whats_new_util_unittest.cc",
     ]
@@ -8561,7 +8565,6 @@
       "../browser/enterprise/connectors/device_trust/key_management/core/persistence:unit_tests",
       "../browser/enterprise/connectors/device_trust/key_management/installer:unit_tests",
       "../browser/enterprise/connectors/device_trust/signals/decorators/browser:unit_tests",
-      "//chrome/browser/ui/webui/app_home:mojo_bindings",
       "//components/device_signals/core/browser",
       "//components/device_signals/core/browser:test_support",
       "//components/device_signals/core/common:features",
@@ -11150,7 +11153,6 @@
     ]
     deps = [
       ":ash_crosapi_tests_runner",
-      ":test_ash_chrome",
       "//ash/constants",
       "//base",
       "//base/test:test_support",
@@ -11162,6 +11164,7 @@
       "//mojo/public/cpp/system",
       "//testing/gtest",
     ]
+    data_deps = [ ":test_ash_chrome" ]
   }
 
   # This target is for Ash-side crosapi browser testing.
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/BUILD.gn b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/BUILD.gn
index 7a63146..225a5a9df 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/BUILD.gn
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/BUILD.gn
@@ -44,6 +44,8 @@
     "read_directory/test.js",
     "read_file/sw.js",
     "read_file/test.js",
+    "unmount/sw.js",
+    "unmount/test.js",
     "write_file/sw.js",
     "write_file/test.js",
   ]
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/add_watcher/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/add_watcher/test.js
index 34b5b6215..4213801 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/add_watcher/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/add_watcher/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {getAllFsInfos, mountTestFileSystem, promisifyWithLastError} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, getAllFsInfos, mountTestFileSystem, promisifyWithLastError} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
 /** @param {!Entry} entry */
@@ -22,17 +22,15 @@
       const fileEntry =
           await fileSystem.getFileEntry(fileName, {create: false});
       const watching = await addFileWatch(fileEntry);
+
       chrome.test.assertTrue(watching);
-
       const fsInfos = await getAllFsInfos();
-
       chrome.test.assertEq(1, fsInfos.length);
       chrome.test.assertEq(1, fsInfos[0].watchers.length);
       const watcher = fsInfos[0].watchers[0];
-      chrome.test.assertEq('/' + fileName, watcher.entryPath);
+      chrome.test.assertEq(`/${fileName}`, watcher.entryPath);
       chrome.test.assertFalse(watcher.recursive);
       chrome.test.assertEq(undefined, /**@type {!Object} */ (watcher).tag);
-
       chrome.test.succeed();
     },
 
@@ -45,37 +43,32 @@
 
       const fileEntry =
           await fileSystem.getFileEntry(fileName, {create: false});
-      try {
-        await addFileWatch(fileEntry);
-        chrome.test.fail('Adding file watcher should have failed.');
-      } catch (error) {
-        chrome.test.assertEq('Unknown error.', error.message);
+      const error = await catchError(addFileWatch(fileEntry));
 
-        // Shouldn't change the number of watchers.
-        fsInfos = await getAllFsInfos();
-        chrome.test.assertEq(1, fsInfos.length);
-        chrome.test.assertEq(1, fsInfos[0].watchers.length);
-
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(
+          !!error, 'Adding file watcher should have failed.');
+      chrome.test.assertEq('Unknown error.', error.message);
+      // Shouldn't change the number of watchers.
+      fsInfos = await getAllFsInfos();
+      chrome.test.assertEq(1, fsInfos.length);
+      chrome.test.assertEq(1, fsInfos[0].watchers.length);
+      chrome.test.succeed();
     },
 
     // Add an entry watcher on a broken file, it should fail.
     async function addBrokenFileWatcher() {
       const fileEntry = await fileSystem.getFileEntry(
           TestFileSystemProvider.FILE_FAIL, {create: false});
-      try {
-        await addFileWatch(fileEntry);
-        chrome.test.fail('Adding file watcher should have failed.');
-      } catch (error) {
-        chrome.test.assertEq('Unknown error.', error.message);
-        // Shouldn't change the number of watchers.
-        const fsInfos = await getAllFsInfos();
-        chrome.test.assertEq(1, fsInfos.length);
-        chrome.test.assertEq(1, fsInfos[0].watchers.length);
+      const error = await catchError(addFileWatch(fileEntry));
 
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(
+          !!error, 'Adding file watcher should have failed.');
+      chrome.test.assertEq('Unknown error.', error.message);
+      // Shouldn't change the number of watchers.
+      const fsInfos = await getAllFsInfos();
+      chrome.test.assertEq(1, fsInfos.length);
+      chrome.test.assertEq(1, fsInfos[0].watchers.length);
+      chrome.test.succeed();
     }
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/big_file/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/big_file/test.js
index 902ed82..81683ec 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/big_file/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/big_file/test.js
@@ -22,6 +22,7 @@
       // Read 10 bytes past the max unsigned 32-bit integer offset.
       const offset = 2 ** 32 + 100;
       const text = await readTextFromBlob(file.slice(offset, offset + 10));
+
       // Check the provider got the read request at the correct offset.
       chrome.test.assertEq(
           offset,
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/configure/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/configure/test.js
index eb5f2468..2ed3fb4 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/configure/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/configure/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem, promisifyWithLastError, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, promisifyWithLastError, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -17,6 +17,7 @@
     async function configureConfigurable() {
       let providers =
           await promisifyWithLastError(chrome.fileManagerPrivate.getProviders);
+
       // Filter out native providers.
       providers = providers.filter(
           provider =>
@@ -30,9 +31,11 @@
       chrome.test.assertTrue(providers[0].configurable);
       chrome.test.assertFalse(providers[0].multipleMounts);
       chrome.test.assertEq('device', providers[0].source);
+
       await new Promise(
           resolve => chrome.fileManagerPrivate.configureVolume(
               fileSystem.volumeInfo.volumeId, resolve));
+
       chrome.test.succeed();
     },
 
@@ -42,6 +45,7 @@
       await promisifyWithLastError(
           chrome.fileManagerPrivate.configureVolume,
           fileSystem.volumeInfo.volumeId);
+
       await remoteProvider.waitForEvent('onConfigureRequested');
       chrome.test.succeed();
     },
@@ -53,15 +57,14 @@
           'onConfigureRequestedError',
           chrome.fileSystemProvider.ProviderError.FAILED,
       );
-      try {
-        await promisifyWithLastError(
-            chrome.fileManagerPrivate.configureVolume,
-            fileSystem.volumeInfo.volumeId);
-        chrome.test.fail('Configuration should have failed.');
-      } catch (e) {
-        chrome.test.assertEq('Failed to complete configuration.', e.message);
-        chrome.test.succeed();
-      }
+
+      const error = await catchError(promisifyWithLastError(
+          chrome.fileManagerPrivate.configureVolume,
+          fileSystem.volumeInfo.volumeId));
+
+      chrome.test.assertTrue(!!error, 'Configuration should have failed.');
+      chrome.test.assertEq('Failed to complete configuration.', error.message);
+      chrome.test.succeed();
     }
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/copy_entry/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/copy_entry/test.js
index 65cdac6..d075307f1 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/copy_entry/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/copy_entry/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -21,9 +21,11 @@
       const sourceEntry =
           await fileSystem.getFileEntry(srcPath, {create: false});
       chrome.test.assertFalse(sourceEntry.isDirectory);
+
       const targetEntry = await new Promise(
           (resolve, reject) => sourceEntry.copyTo(
               fileSystem.fileSystem.root, dstPath, resolve, reject));
+
       chrome.test.assertEq(dstPath, targetEntry.name);
       chrome.test.assertFalse(targetEntry.isDirectory);
       chrome.test.succeed();
@@ -34,15 +36,14 @@
       const sourceEntry =
           await fileSystem.getFileEntry(srcPath, {create: false});
       chrome.test.assertFalse(sourceEntry.isDirectory);
-      try {
-        await new Promise(
-            (resolve, reject) => sourceEntry.copyTo(
-                fileSystem.fileSystem.root, dstPath, resolve, reject));
-        chrome.test.fail('Succeeded, but should fail.');
-      } catch (e) {
-        chrome.test.assertEq('InvalidModificationError', e.name);
-        chrome.test.succeed();
-      }
+
+      const error = await catchError(new Promise(
+          (resolve, reject) => sourceEntry.copyTo(
+              fileSystem.fileSystem.root, dstPath, resolve, reject)));
+
+      chrome.test.assertTrue(!!error, 'Succeeded, but should fail.');
+      chrome.test.assertEq('InvalidModificationError', error.name);
+      chrome.test.succeed();
     },
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/create_file/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/create_file/test.js
index 88f8bb6..b60e12d5 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/create_file/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/create_file/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 
 /**
  * @type {Object}
@@ -71,14 +71,12 @@
 
     // Create a file which exists, exclusively. Should fail.
     async function createFileExistsError() {
-      try {
-        await getFileEntry(TESTING_FILE.name, {create: true, exclusive: true});
+      const error = await catchError(
+          getFileEntry(TESTING_FILE.name, {create: true, exclusive: true}));
 
-        chrome.test.fail('Created a file, but should fail.');
-      } catch (e) {
-        chrome.test.assertEq('InvalidModificationError', e.name);
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(!!error, 'Created a file, but should fail.');
+      chrome.test.assertEq('InvalidModificationError', error.name);
+      chrome.test.succeed();
     }
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/delete_entry/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/delete_entry/test.js
index 08a0ed6..c4d8aef 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/delete_entry/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/delete_entry/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -51,7 +51,9 @@
       });
       chrome.test.assertEq(TEST_FILE, entry.name);
       chrome.test.assertFalse(entry.isDirectory);
+
       await new Promise((resolve, reject) => entry.remove(resolve, reject));
+
       chrome.test.succeed();
     },
     // Delete a directory which has contents, non-recursively. Should fail.
@@ -60,13 +62,14 @@
           await fileSystem.getDirectoryEntry(TEST_DIR, {create: false});
       chrome.test.assertEq(TEST_DIR, entry.name);
       chrome.test.assertTrue(entry.isDirectory);
-      try {
-        await new Promise((resolve, reject) => entry.remove(resolve, reject));
-        chrome.test.fail('Unexpectedly succeded to remove a directory.');
-      } catch (e) {
-        chrome.test.assertEq('InvalidModificationError', e.name);
-        chrome.test.succeed();
-      }
+
+      const error = await catchError(
+          new Promise((resolve, reject) => entry.remove(resolve, reject)));
+
+      chrome.test.assertTrue(
+          !!error, 'Unexpectedly succeded to remove a directory.');
+      chrome.test.assertEq('InvalidModificationError', error.name);
+      chrome.test.succeed();
     },
 
     // Delete a directory which has contents, recursively. Should succeed.
@@ -75,8 +78,10 @@
           await fileSystem.getDirectoryEntry(TEST_DIR, {create: false});
       chrome.test.assertEq(TEST_DIR, entry.name);
       chrome.test.assertTrue(entry.isDirectory);
+
       await new Promise(
           (resolve, reject) => entry.removeRecursively(resolve, reject));
+
       chrome.test.succeed();
     },
   ]);
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/evil/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/evil/test.js
index e23c7a9..00e014c 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/evil/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/evil/test.js
@@ -1,7 +1,7 @@
 // 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 {mountTestFileSystem, openFile, readTextFromBlob, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, openFile, readTextFromBlob, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -20,13 +20,11 @@
       const file = await openFile(fileEntry);
       // Read 1 KB of data.
       const fileSlice = file.slice(0, 1024);
-      try {
-        await readTextFromBlob(fileSlice);
-        chrome.test.fail('Reading should fail.');
-      } catch (e) {
-        chrome.test.assertEq('NotReadableError', e.name);
-        chrome.test.succeed();
-      }
+      const error = await catchError(readTextFromBlob(fileSlice));
+
+      chrome.test.assertTrue(!!error, 'Reading should fail.');
+      chrome.test.assertEq('NotReadableError', error.name);
+      chrome.test.succeed();
     },
 
     // Tests that calling a success callback with a non-existing request id
@@ -39,13 +37,11 @@
       const file = await openFile(fileEntry);
       // Read 1 KB of data.
       const fileSlice = file.slice(0, 1024);
-      try {
-        await readTextFromBlob(fileSlice);
-        chrome.test.fail('Reading should fail.');
-      } catch (e) {
-        chrome.test.assertEq('NotReadableError', e.name);
-        chrome.test.succeed();
-      }
+      const error = await catchError(readTextFromBlob(fileSlice));
+
+      chrome.test.assertTrue(!!error, 'Reading should fail.');
+      chrome.test.assertEq('NotReadableError', error.name);
+      chrome.test.succeed();
     },
 
     // Test that reading from files with negative size is not allowed (empty
@@ -59,25 +55,24 @@
       // Read 1 KB of data.
       const fileSlice = file.slice(0, 1024);
       const text = await readTextFromBlob(fileSlice);
+
       chrome.test.assertEq('', text);
       chrome.test.succeed();
     },
 
     // Tests that accesses to  files containing ".." do not work.
     async function relativeName() {
-      try {
-        await fileSystem.getFileEntry(
-            '../../../b.txt',
-            {create: false},
-        );
-        chrome.test.fail('Opening a file should fail.');
-      } catch (e) {
-        chrome.test.assertEq('NotFoundError', e.name);
-        // The call to open a file should not even reach the provider.
-        chrome.test.assertEq(
-            0, await remoteProvider.getEventCount('onFileOpenRequested'));
-        chrome.test.succeed();
-      }
+      const error = await catchError(fileSystem.getFileEntry(
+          '../../../b.txt',
+          {create: false},
+          ));
+
+      chrome.test.assertTrue(!!error, 'Opening a file should fail.');
+      chrome.test.assertEq('NotFoundError', error.name);
+      // The call to open a file should not even reach the provider.
+      chrome.test.assertEq(
+          0, await remoteProvider.getEventCount('onFileOpenRequested'));
+      chrome.test.succeed();
     }
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_all/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_all/test.js
index e211d2be..b78a932 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_all/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_all/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {getAllFsInfos, getFsInfoById, mountTestFileSystem, openFile, remoteProvider, startReadTextFromBlob, unmount} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, getAllFsInfos, getFsInfoById, mountTestFileSystem, openFile, remoteProvider, startReadTextFromBlob, unmount} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -20,6 +20,7 @@
       startReadTextFromBlob(file);
       await remoteProvider.waitForEvent('onOpenFileRequested');
 
+      // Check output of chrome.fileSystemProvider.getAll().
       const fsInfos = await getAllFsInfos();
       chrome.test.assertEq(1, fsInfos.length);
       chrome.test.assertEq(
@@ -35,6 +36,7 @@
           chrome.fileSystemProvider.OpenFileMode.READ,
           fsInfos[0].openedFiles[0].mode);
 
+      // Check output of chrome.fileSystemProvider.get().
       const fsInfo = await getFsInfoById(TestFileSystemProvider.FILESYSTEM_ID);
       chrome.test.assertEq(
           TestFileSystemProvider.FILESYSTEM_ID, fsInfo.fileSystemId);
@@ -57,33 +59,40 @@
     async function unmountSuccess() {
       await unmount(TestFileSystemProvider.FILESYSTEM_ID);
 
+      // Check output of chrome.fileSystemProvider.getAll().
       chrome.test.assertEq([], await getAllFsInfos());
-      try {
-        await getFsInfoById(TestFileSystemProvider.FILESYSTEM_ID);
-        chrome.test.fail('Found the filesystem that was unmounted.');
-      } catch (e) {
-        chrome.test.assertEq('NOT_FOUND', e.message);
-        chrome.test.succeed();
-      }
+
+      // Check output of chrome.fileSystemProvider.get().
+      const error =
+          await catchError(getFsInfoById(TestFileSystemProvider.FILESYSTEM_ID));
+      chrome.test.assertTrue(
+          !!error, 'Found the filesystem that was unmounted.');
+      chrome.test.assertEq('NOT_FOUND', error.message);
+
+      chrome.test.succeed();
     },
 
     // Verifies that if mounting fails, then the file system is not added to the
     // getAll() list.
     async function mountError() {
-      try {
-        await chrome.fileSystemProvider.mount(
-            {fileSystemId: '', displayName: ''});
-        chrome.test.fail('Mount operation should have failed.');
-      } catch (e) {
-        chrome.test.assertEq('INVALID_OPERATION', e.message);
-      }
+      const mountError = await catchError(
+          /** !Promise<?> */ chrome.fileSystemProvider.mount(
+              {fileSystemId: '', displayName: ''}));
+
+      chrome.test.assertTrue(
+          !!mountError, 'Mount operation should have failed.');
+      chrome.test.assertEq('INVALID_OPERATION', mountError.message);
+
+      // Check output of chrome.fileSystemProvider.getAll().
       chrome.test.assertEq([], await getAllFsInfos());
-      try {
-        await getFsInfoById(TestFileSystemProvider.FILESYSTEM_ID);
-        chrome.test.fail('Found the filesystem that was unmounted.');
-      } catch (e) {
-        chrome.test.assertEq('NOT_FOUND', e.message);
-      }
+
+      // Check output of chrome.fileSystemProvider.get().
+      const lookupError =
+          await catchError(getFsInfoById(TestFileSystemProvider.FILESYSTEM_ID));
+      chrome.test.assertTrue(
+          !!lookupError, 'Found the filesystem that was unmounted.');
+      chrome.test.assertEq('NOT_FOUND', lookupError.message);
+
       chrome.test.succeed();
     },
   ]);
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_metadata/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_metadata/test.js
index 09fab7f..8d417f9 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_metadata/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/get_metadata/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -23,6 +23,7 @@
     // Read metadata of the root.
     async function getRootMetadataSuccess() {
       const meta = await getMetadata(fileSystem.fileSystem.root);
+
       chrome.test.assertEq(0, meta.size);
       chrome.test.assertEq(
           new Date(2014, 4, 28, 10, 39, 15).toString(),
@@ -33,11 +34,11 @@
     // Read metadata of an existing testing file.
     async function getFileMetadataSuccess() {
       await remoteProvider.resetState();
+
       const fileEntry = await fileSystem.getFileEntry(
-          TestFileSystemProvider.FILE_READ_SUCCESS,
-          {create: false},
-      );
+          TestFileSystemProvider.FILE_READ_SUCCESS, {create: false});
       const meta = await getMetadata(fileEntry);
+
       chrome.test.assertEq(
           TestFileSystemProvider.INITIAL_TEXT.length, meta.size);
       chrome.test.assertEq(
@@ -52,10 +53,10 @@
     // easy way to verify an incorrect modification time at early stage.
     async function getFileMetadataWrongTimeSuccess() {
       const fileEntry = await fileSystem.getFileEntry(
-          TestFileSystemProvider.FILE_INVALID_DATE,
-          {create: false},
-      );
+          TestFileSystemProvider.FILE_INVALID_DATE, {create: false});
+
       const meta = await getMetadata(fileEntry);
+
       chrome.test.assertTrue(Number.isNaN(meta.modificationTime.getTime()));
       chrome.test.succeed();
     },
@@ -65,15 +66,13 @@
     async function getFileMetadataNotFound() {
       const TEST_DIR = 'non-existent';
       await remoteProvider.resetState();
-      try {
-        await fileSystem.getDirectoryEntry(
-            TEST_DIR,
-            {create: false},
-        );
-        chrome.test.fail('Getting a directory should have failed.');
-      } catch (e) {
-        chrome.test.assertEq('NotFoundError', e.name);
-      }
+
+      const error = await catchError(
+          fileSystem.getDirectoryEntry(TEST_DIR, {create: false}));
+
+      chrome.test.assertTrue(
+          !!error, 'Getting a directory should have failed.');
+      chrome.test.assertEq('NotFoundError', error.name);
       const {entryPath} =
           await remoteProvider.waitForEvent('onGetMetadataRequested');
       chrome.test.assertEq(`/${TEST_DIR}`, entryPath);
@@ -85,15 +84,13 @@
     // fetching metadata.
     async function getFileMetadataWrongType() {
       await remoteProvider.resetState();
-      try {
-        await fileSystem.getDirectoryEntry(
-            TestFileSystemProvider.FILE_READ_SUCCESS,
-            {create: false},
-        );
-        chrome.test.fail('Getting a directory should have failed.');
-      } catch (e) {
-        chrome.test.assertEq('TypeMismatchError', e.name);
-      }
+
+      const error = await catchError(fileSystem.getDirectoryEntry(
+          TestFileSystemProvider.FILE_READ_SUCCESS, {create: false}));
+
+      chrome.test.assertTrue(
+          !!error, 'Getting a directory should have failed.');
+      chrome.test.assertEq('TypeMismatchError', error.name);
       const {entryPath} =
           await remoteProvider.waitForEvent('onGetMetadataRequested');
       chrome.test.assertEq(
@@ -104,10 +101,10 @@
     // Resolving a file should only request is_directory field.
     async function getMetadataForGetFile() {
       await remoteProvider.resetState();
+
       await fileSystem.getFileEntry(
-          `/${TestFileSystemProvider.FILE_ONLY_TYPE}`,
-          {create: false},
-      );
+          `/${TestFileSystemProvider.FILE_ONLY_TYPE}`, {create: false});
+
       const options =
           await remoteProvider.waitForEvent('onGetMetadataRequested');
       chrome.test.assertEq(options.isDirectory, true);
@@ -123,16 +120,15 @@
     // callback is invoked.
     async function getMetadataMissingFields() {
       await remoteProvider.resetState();
+
       const fileEntry = await fileSystem.getFileEntry(
           `/${TestFileSystemProvider.FILE_ONLY_TYPE_AND_SIZE}`,
-          {create: false},
-      );
-      try {
-        await getMetadata(fileEntry);
-        chrome.test.fail('Getting metadata should have failed.');
-      } catch (e) {
-        chrome.test.assertEq('InvalidStateError', e.name);
-      }
+          {create: false});
+
+      const error = await catchError(getMetadata(fileEntry));
+
+      chrome.test.assertTrue(!!error, 'Getting metadata should have failed.');
+      chrome.test.assertEq('InvalidStateError', error.name);
       const options =
           await remoteProvider.waitForEvent('onGetMetadataRequested');
       chrome.test.assertEq(options.isDirectory, true);
@@ -147,16 +143,16 @@
     // Fetch only requested fields.
     async function getEntryPropertiesFewFields() {
       await remoteProvider.resetState();
+
       const fileEntry = await fileSystem.getFileEntry(
           `/${TestFileSystemProvider.FILE_ONLY_TYPE_AND_SIZE}`,
-          {create: false},
-      );
+          {create: false});
       const fileProperties = await new Promise(
           resolve => chrome.fileManagerPrivate.getEntryProperties(
               [fileEntry], ['size'], resolve));
+
       chrome.test.assertEq(1, fileProperties.length);
       chrome.test.assertEq(1024 * 4, fileProperties[0].size);
-
       // The first call is from getFileEntry.
       await remoteProvider.waitForEvent('onGetMetadataRequested');
       // The second call is from getEntryProperties.
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/mount/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/mount/test.js
index e9c09fb3..e1eb73e 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/mount/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/mount/test.js
@@ -37,6 +37,7 @@
     async function goodDisplayName() {
       await mount(
           {fileSystemId: 'file-system-id', displayName: 'file-system-name'});
+
       chrome.test.succeed();
     },
 
@@ -44,6 +45,7 @@
     async function emptyDisplayName() {
       const e = await catchError(
           mount({fileSystemId: 'file-system-id-2', displayName: ''}));
+
       chrome.test.assertTrue(!!e, 'Mount expected to fail.');
       chrome.test.assertEq('INVALID_OPERATION', e.message);
       chrome.test.succeed();
@@ -53,6 +55,7 @@
     async function emptyFileSystemId() {
       const e = await catchError(
           mount({fileSystemId: '', displayName: 'File System Name'}));
+
       chrome.test.assertTrue(!!e, 'Mount expected to fail.');
       chrome.test.assertEq('INVALID_OPERATION', e.message);
       chrome.test.succeed();
@@ -66,6 +69,7 @@
         displayName: 'File System Name',
         openedFilesLimit: 10
       });
+
       chrome.test.succeed();
     },
 
@@ -77,6 +81,7 @@
         displayName: 'File System Name',
         openedFilesLimit: 0
       });
+
       chrome.test.succeed();
     },
 
@@ -88,6 +93,7 @@
         displayName: 'File System Name',
         openedFilesLimit: -1
       }));
+
       chrome.test.assertTrue(!!e, 'Mount expected to fail.');
       chrome.test.assertEq('INVALID_OPERATION', e.message);
       chrome.test.succeed();
@@ -102,6 +108,7 @@
         fileSystemId,
         displayName: 'caramel-candy.zip',
       });
+
       const volumeInfo = await getVolumeInfo(fileSystemId);
       chrome.test.assertTrue(volumeInfo.isReadOnly);
       chrome.test.succeed();
@@ -116,6 +123,7 @@
         displayName: 'caramel-fudges.zip',
         writable: true,
       });
+
       const volumeInfo = await getVolumeInfo(fileSystemId);
       chrome.test.assertFalse(volumeInfo.isReadOnly);
       chrome.test.succeed();
@@ -129,6 +137,7 @@
         displayName: 'read-only.zip',
         writable: false,
       });
+
       const volumeInfo = await getVolumeInfo(fileSystemId);
       chrome.test.assertTrue(volumeInfo.isReadOnly);
       chrome.test.succeed();
@@ -136,22 +145,26 @@
 
     // Checks that providing supportsNotifyTag=false|true persists as requested.
     async function supportsNotifyTag() {
+      // Test with supportsNotifyTag=true
       const taggedFilesystemId = 'tagged-fs';
       await mount({
         fileSystemId: taggedFilesystemId,
         displayName: 'tagged-fs.zip',
         supportsNotifyTag: true,
       });
+
       const fsInfoTagged = await getFsInfoById(taggedFilesystemId);
       chrome.test.assertTrue(!!fsInfoTagged);
       chrome.test.assertEq(fsInfoTagged.supportsNotifyTag, true);
 
+      // Test with supportsNotifyTag=false
       const nonTaggedFilesystemId = 'non-tagged-fs';
       await mount({
         fileSystemId: nonTaggedFilesystemId,
         displayName: 'non-tagged-fs.zip',
         supportsNotifyTag: false,
       });
+
       const fsInfoNonTagged = await getFsInfoById(nonTaggedFilesystemId);
       chrome.test.assertTrue(!!fsInfoNonTagged);
       chrome.test.assertEq(fsInfoNonTagged.supportsNotifyTag, false);
@@ -163,6 +176,7 @@
     // mount requests should succeed, except the last one which should fail with
     // a security error.
     async function stressMountTest() {
+      // Mount file systems up to the limit.
       const alreadyMountedFileSystems = 8;  // By previous tests.
       const maxFileSystems = 16;
       for (let i = alreadyMountedFileSystems; i < maxFileSystems; i++) {
@@ -172,10 +186,12 @@
         });
       }
 
+      // One over the limit should fail.
       const e = await catchError(mount({
         fileSystemId: 'over-the-limit-fs-id',
         displayName: 'Over The Limit File System'
       }));
+
       chrome.test.assertTrue(!!e, 'Mount expected to fail.');
       chrome.test.assertEq('TOO_MANY_OPENED', e.message);
       chrome.test.succeed();
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/move_entry/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/move_entry/test.js
index 536101a..558486c 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/move_entry/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/move_entry/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -31,19 +31,19 @@
       const sourceEntry =
           await fileSystem.getFileEntry(srcPath, {create: false});
       chrome.test.assertFalse(sourceEntry.isDirectory);
+
       const targetEntry = await new Promise(
           (resolve, reject) => sourceEntry.moveTo(
               fileSystem.fileSystem.root, dstPath, resolve, reject));
+      // The source file should be deleted.
+      const error =
+          await catchError(fileSystem.getFileEntry(srcPath, {create: false}));
+
       chrome.test.assertEq(dstPath, targetEntry.name);
       chrome.test.assertFalse(targetEntry.isDirectory);
-      // The source file should be deleted.
-      try {
-        await fileSystem.getFileEntry(srcPath, {create: false});
-        chrome.test.fail('Source file not deleted.');
-      } catch (e) {
-        chrome.test.assertEq('NotFoundError', e.name);
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(!!error, 'Source file not deleted.');
+      chrome.test.assertEq('NotFoundError', error.name);
+      chrome.test.succeed();
     },
 
     // Move an existing file to a location which already holds a file. Should
@@ -52,15 +52,14 @@
       const sourceEntry =
           await fileSystem.getFileEntry(FILE_MOVE_FAIL, {create: false});
       chrome.test.assertFalse(sourceEntry.isDirectory);
-      try {
-        await new Promise(
-            (resolve, reject) => sourceEntry.moveTo(
-                fileSystem.fileSystem.root, dstPath, resolve, reject));
-        chrome.test.fail('Succeeded, but should fail.');
-      } catch (e) {
-        chrome.test.assertEq('InvalidModificationError', e.name);
-        chrome.test.succeed();
-      }
+
+      const error = await catchError(new Promise(
+          (resolve, reject) => sourceEntry.moveTo(
+              fileSystem.fileSystem.root, dstPath, resolve, reject)));
+
+      chrome.test.assertTrue(!!error, 'Succeeded, but should fail.');
+      chrome.test.assertEq('InvalidModificationError', error.name);
+      chrome.test.succeed();
     },
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/provider.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/provider.js
index 3652b7ca..120f61f 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/provider.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/provider.js
@@ -261,6 +261,7 @@
     this.setHandlerEnabled('onReadDirectoryRequested', true);
     this.setHandlerEnabled('onReadFileRequested', true);
     this.setHandlerEnabled('onRemoveWatcherRequested', true);
+    this.setHandlerEnabled('onUnmountRequested', true);
     this.setHandlerEnabled('onWriteFileRequested', true);
   }
 
@@ -886,6 +887,24 @@
   };
 
   /**
+   * FSP: requests to unmount this filesystem.
+   *
+   * @param {!chrome.fileSystemProvider.UnmountRequestedOptions} options
+   * @param {function()} onSuccess Success callback.
+   * @param {function(chrome.fileSystemProvider.ProviderError)} onError Error
+   *     callback.
+   */
+  onUnmountRequested(options, onSuccess, onError) {
+    this.recordEvent('onUnmountRequested', options);
+    const error = this.testConfig['onUnmountRequestedError'];
+    if (error) {
+      onError(error);
+    } else {
+      onSuccess();
+    }
+  };
+
+  /**
    * FSP: requests writing contents to a file, previously opened with <code>
    * openRequestId</code>.
    *
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_directory/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_directory/test.js
index 5f37b45..054d992 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_directory/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_directory/test.js
@@ -1,7 +1,7 @@
 // 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 {mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -73,6 +73,7 @@
       const dirEntry = await fileSystem.getDirectoryEntry(
           TESTING_HELLO_DIR.name, {create: false});
       const entries = await readAllEntries(dirEntry);
+
       chrome.test.assertEq(2, entries.length);
       chrome.test.assertTrue(entries[0].isFile);
       chrome.test.assertEq('tiramisu.txt', entries[0].name);
@@ -82,16 +83,17 @@
       chrome.test.assertEq('/hello/candies', entries[1].fullPath);
       chrome.test.succeed();
     },
+
     // Read contents of a directory which does not exist, what should return an
     // error.
     async function readEntriesError() {
-      try {
-        await fileSystem.getDirectoryEntry('cranberries', {create: false});
-        chrome.test.fail('Succeeded getting a non-existent directory entry.');
-      } catch (e) {
-        chrome.test.assertEq('NotFoundError', e.name);
-        chrome.test.succeed();
-      }
+      const error = await catchError(
+          fileSystem.getDirectoryEntry('cranberries', {create: false}));
+
+      chrome.test.assertTrue(
+          !!error, 'Succeeded getting a non-existent directory entry.');
+      chrome.test.assertEq('NotFoundError', error.name);
+      chrome.test.succeed();
     }
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_file/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_file/test.js
index 9cfd5598..c6b4a01f 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_file/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/read_file/test.js
@@ -1,7 +1,7 @@
 // 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 {mountTestFileSystem, openFile, readTextFromBlob, remoteProvider, startReadTextFromBlob} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, openFile, readTextFromBlob, remoteProvider, startReadTextFromBlob} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -18,6 +18,7 @@
       );
       const file = await openFile(fileEntry);
       const text = await readTextFromBlob(file);
+
       chrome.test.assertEq(TestFileSystemProvider.INITIAL_TEXT, text);
       chrome.test.succeed();
     },
@@ -47,6 +48,7 @@
         chrome.test.assertTrue(await remoteProvider.getOpenedFiles() <= 2);
         await remoteProvider.continueRequest(requestId);
       }
+
       // All reads should complete successfully.
       for (const promise of reads) {
         chrome.test.assertEq(
@@ -65,19 +67,19 @@
           {create: false},
       );
       const file = await openFile(fileEntry);
-      try {
-        await readTextFromBlob(file);
-        chrome.test.fail('Unexpectedly succeeded to read a broken file.');
-      } catch (e) {
-        chrome.test.assertEq('NotReadableError', e.name);
-        chrome.test.succeed();
-      }
+      const error = await catchError(readTextFromBlob(file));
+
+      chrome.test.assertTrue(
+          !!error, 'Unexpectedly succeeded to read a broken file.');
+      chrome.test.assertEq('NotReadableError', error.name);
+      chrome.test.succeed();
     },
 
     // Abort reading a file with a registered abort handler. Should result in a
     // gracefully terminated reading operation.
     async function abortReadingSuccess() {
       await remoteProvider.resetState();
+
       const fileEntry = await fileSystem.getFileEntry(
           TestFileSystemProvider.FILE_BLOCKS_FOREVER,
           {create: false},
@@ -90,14 +92,13 @@
       reader.abort();
       await remoteProvider.waitForEvent('onAbortRequested');
       await remoteProvider.waitForEvent('onCloseFileRequested');
+      const error = await catchError(promise);
+
       chrome.test.assertEq(0, await remoteProvider.getOpenedFiles());
-      try {
-        await promise;
-        chrome.test.fail('Unexpectedly succeeded after read aborted.');
-      } catch (e) {
-        chrome.test.assertEq('AbortError', e.name);
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(
+          !!error, 'Unexpectedly succeeded after read aborted.');
+      chrome.test.assertEq('AbortError', error.name);
+      chrome.test.succeed();
     },
 
     // Abort opening a file while trying to read it without an abort handler
@@ -105,6 +106,7 @@
     async function abortViaCloseSuccess() {
       await remoteProvider.resetState();
       await remoteProvider.setHandlerEnabled('onAbortRequested', false);
+
       const fileEntry = await fileSystem.getFileEntry(
           TestFileSystemProvider.FILE_BLOCKS_FOREVER,
           {create: false, exclusive: false},
@@ -116,14 +118,13 @@
       chrome.test.assertEq(1, await remoteProvider.getOpenedFiles());
       reader.abort();
       await remoteProvider.waitForEvent('onCloseFileRequested');
+      const error = await catchError(promise);
+
       chrome.test.assertEq(0, await remoteProvider.getOpenedFiles());
-      try {
-        await promise;
-        chrome.test.fail('Unexpectedly succeeded after read aborted.');
-      } catch (e) {
-        chrome.test.assertEq('AbortError', e.name);
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(
+          !!error, 'Unexpectedly succeeded after read aborted.');
+      chrome.test.assertEq('AbortError', error.name);
+      chrome.test.succeed();
     },
 
     // Abort opening a file while trying to read it without an abort handler
@@ -133,6 +134,8 @@
       await remoteProvider.resetState();
       await fileSystem.remount(/*openedFilesLimit=*/ 1);
       await remoteProvider.setHandlerEnabled('onAbortRequested', false);
+
+      // Start reading two files, the first read should get stuck on open.
       const fileEntry1 = await fileSystem.getFileEntry(
           TestFileSystemProvider.FILE_STALL_OPEN,
           {create: false, exclusive: false},
@@ -141,13 +144,11 @@
           TestFileSystemProvider.FILE_READ_SUCCESS,
           {create: false, exclusive: false},
       );
-      // Start reading both files, the first should get stuck on open.
       const read1 = startReadTextFromBlob(await openFile(fileEntry1));
       const read2 = startReadTextFromBlob(await openFile(fileEntry2));
       const openRequest1 =
           await remoteProvider.waitForEvent('onOpenFileRequested');
-      // Wait until the request is blocked inside the open call and abort the
-      // read.
+      // Wait until the first open request is blocked.
       const {requestId} =
           await remoteProvider.waitForEvent('onOpenFileRequestedStalled');
       chrome.test.assertEq(1, await remoteProvider.getOpenedFiles());
@@ -155,20 +156,19 @@
       // immediately close.
       read1.reader.abort();
       await remoteProvider.continueRequest(requestId);
-      // Should be able to open and read the second file now.
+
+      // The second read request should continue successfully.
       const openRequest2 =
           await remoteProvider.waitForEvent('onOpenFileRequested');
       chrome.test.assertEq(
-          '/' + TestFileSystemProvider.FILE_READ_SUCCESS,
+          `/${TestFileSystemProvider.FILE_READ_SUCCESS}`,
           openRequest2.filePath);
       chrome.test.assertEq(
           openRequest2.requestId,
           (await remoteProvider.waitForEvent('onReadFileRequested'))
               .openRequestId);
       chrome.test.assertEq(
-          TestFileSystemProvider.INITIAL_TEXT,
-          await read2.promise,
-      )
+          TestFileSystemProvider.INITIAL_TEXT, await read2.promise)
       // The first file should have been closed.
       chrome.test.assertEq(
           openRequest1.requestId,
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/manifest.json
new file mode 100644
index 0000000..6ce0312
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/manifest.json
@@ -0,0 +1,15 @@
+{
+  "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDOuXEIuoK1kAkBe0SKiJn/N9oNn3oUxGa4dwj40MnJqPn+w0aR2vuyocm0R4Drp67aYwtLjOVPF4CICRq6ICP6eU07gGwQxGdZ7HJASXV8hm0tab5I70oJmRLfFJyVAMCeWlFaOGq05v2i6EbifZM0qO5xALKNGQt+yjXi5INM5wIBIw==",
+  "name": "chrome.fileSystemProvider.unmount",
+  "version": "0.1",
+  "manifest_version": 3,
+  "description": "Test for chrome.fileSystemProvider.unmount() in service workers.",
+  "permissions": ["fileSystemProvider", "fileManagerPrivate"],
+  "file_system_provider_capabilities": {
+    "source": "device"
+  },
+  "background": {
+    "service_worker": "sw.js",
+    "type": "module"
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/sw.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/sw.js
new file mode 100644
index 0000000..ac074135
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/sw.js
@@ -0,0 +1,7 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {serviceWorkerMain} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
+
+serviceWorkerMain(self);
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/test.html b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/test.html
new file mode 100644
index 0000000..9980fda0
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/test.html
@@ -0,0 +1 @@
+<script type="module" src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/test.js
new file mode 100644
index 0000000..f402160
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/unmount/test.js
@@ -0,0 +1,109 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {catchError, getVolumeInfo, promisifyWithLastError, remoteProvider, unmount} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+
+/**
+ * Listens for a single onMountCompleted event.
+ * @returns {!Promise<!chrome.fileManagerPrivate.MountCompletedEvent>}
+ */
+async function nextOnMountCompletedEvent() {
+  return new Promise(resolve => {
+    const handler = (e) => {
+      resolve(e);
+      chrome.fileManagerPrivate.onMountCompleted.removeListener(handler);
+    };
+    chrome.fileManagerPrivate.onMountCompleted.addListener(handler);
+  });
+}
+
+async function main() {
+  await navigator.serviceWorker.ready;
+
+  const firstFileSystemId = 'vanilla';
+  const secondFileSystemId = 'ice-cream';
+
+  await Promise.all([
+    chrome.fileSystemProvider.mount(
+        {fileSystemId: firstFileSystemId, displayName: 'vanilla.zip'}),
+    chrome.fileSystemProvider.mount(
+        {fileSystemId: secondFileSystemId, displayName: 'ice-cream.zip'}),
+  ]);
+
+  chrome.test.runTests([
+    // Tests the fileSystemProvider.unmount(). Verifies if the unmount event
+    // is emitted by VolumeManager.
+    async function unmountSuccessful() {
+      const unmountEventPromise = nextOnMountCompletedEvent();
+
+      await unmount(firstFileSystemId);
+
+      const e = await unmountEventPromise;
+      chrome.test.assertEq('unmount', e.eventType);
+      chrome.test.assertEq('success', e.status);
+      // For extension based providers, provider id is the same as
+      // extension id.
+      chrome.test.assertEq(chrome.runtime.id, e.volumeMetadata.providerId);
+      chrome.test.assertEq(firstFileSystemId, e.volumeMetadata.fileSystemId);
+      chrome.test.succeed();
+    },
+
+    // Tests the fileSystemProvider.unmount() with a wrong id. Verifies that
+    // it fails with a correct error code.
+    async function unmountWrongId() {
+      await remoteProvider.resetState();
+      const error = await catchError(unmount('wrong-fs-id'));
+
+      chrome.test.assertTrue(!!error, 'Unmount should have failed.');
+      chrome.test.assertEq('NOT_FOUND', error.message);
+      chrome.test.succeed();
+    },
+
+    // Tests if fileManagerPrivate.removeMount() for provided file systems emits
+    // the onMountRequested() event with correct arguments.
+    async function requestUnmountSuccess() {
+      await remoteProvider.resetState();
+      const volumeInfo = await getVolumeInfo(secondFileSystemId);
+      await promisifyWithLastError(
+          chrome.fileManagerPrivate.removeMount, volumeInfo.volumeId);
+
+      const {fileSystemId} =
+          await remoteProvider.waitForEvent('onUnmountRequested');
+      chrome.test.assertEq(secondFileSystemId, fileSystemId);
+      chrome.test.succeed();
+    },
+
+    // End-to-end test with a failure. Invokes fileSystemProvider.removeMount()
+    // on a provided file system, and verifies (1) if the onMountRequested()
+    // event is called with correct arguments, and (2) if calling onError(),
+    // results in an unmount event fired from the VolumeManager instance.
+    async function requestUnmountError() {
+      await remoteProvider.resetState();
+      // Next call to unmount should return error.
+      await remoteProvider.setConfig(
+          'onUnmountRequestedError',
+          chrome.fileSystemProvider.ProviderError.IN_USE,
+      );
+      const unmountEventPromise = nextOnMountCompletedEvent();
+
+      const volumeInfo = await getVolumeInfo(secondFileSystemId);
+      await promisifyWithLastError(
+          chrome.fileManagerPrivate.removeMount, volumeInfo.volumeId);
+
+      const {fileSystemId} =
+          await remoteProvider.waitForEvent('onUnmountRequested');
+      chrome.test.assertEq(secondFileSystemId, fileSystemId);
+      const e = await unmountEventPromise;
+      chrome.test.assertEq('unmount', e.eventType);
+      chrome.test.assertEq('error_unknown', e.status);
+      // For extension based providers, provider id is the same as
+      // extension id.
+      chrome.test.assertEq(chrome.runtime.id, e.volumeMetadata.providerId);
+      chrome.test.assertEq(secondFileSystemId, e.volumeMetadata.fileSystemId);
+      chrome.test.succeed();
+    }
+  ]);
+}
+
+main();
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/write_file/test.js b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/write_file/test.js
index 16492ea..5660503 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/service_worker/write_file/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/service_worker/write_file/test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
+import {catchError, mountTestFileSystem, remoteProvider} from '/_test_resources/api_test/file_system_provider/service_worker/helpers.js';
 // For shared constants.
 import {TestFileSystemProvider} from '/_test_resources/api_test/file_system_provider/service_worker/provider.js';
 
@@ -58,7 +58,7 @@
 async function main() {
   await navigator.serviceWorker.ready;
   await remoteProvider.addFiles({
-    ['/' + TESTING_TIRAMISU_FILE_NAME]: {
+    [`/${TESTING_TIRAMISU_FILE_NAME}`]: {
       metadata: {
         isDirectory: false,
         name: TESTING_TIRAMISU_FILE_NAME,
@@ -75,12 +75,11 @@
     async function writeNewFileSuccess() {
       const fileEntry = await fileSystem.getFileEntry(
           TESTING_NEW_FILE_NAME, {create: true, exclusive: true});
-
       await writeTextToFile(
           await createWriter(fileEntry), TESTING_TEXT_TO_WRITE);
 
       const newContents =
-          await remoteProvider.getFileContents('/' + TESTING_NEW_FILE_NAME);
+          await remoteProvider.getFileContents(`/${TESTING_NEW_FILE_NAME}`);
       chrome.test.assertEq(TESTING_TEXT_TO_WRITE, newContents);
       chrome.test.succeed();
     },
@@ -89,12 +88,11 @@
     async function overwriteFileSuccess() {
       const fileEntry = await fileSystem.getFileEntry(
           TESTING_TIRAMISU_FILE_NAME, {create: true, exclusive: false});
-
       await writeTextToFile(
           await createWriter(fileEntry), TESTING_TEXT_TO_WRITE);
 
       const newContents = await remoteProvider.getFileContents(
-          '/' + TESTING_TIRAMISU_FILE_NAME);
+          `/${TESTING_TIRAMISU_FILE_NAME}`);
       chrome.test.assertEq(TESTING_TEXT_TO_WRITE, newContents);
       chrome.test.succeed();
     },
@@ -103,13 +101,12 @@
     async function appendFileSuccess() {
       const fileEntry = await fileSystem.getFileEntry(
           TESTING_TIRAMISU_FILE_NAME, {create: false, exclusive: false});
-
       const fileWriter = await createWriter(fileEntry);
       fileWriter.seek(TESTING_TEXT_TO_WRITE.length);
       await writeTextToFile(fileWriter, TESTING_TEXT_TO_WRITE);
 
       const newContents = await remoteProvider.getFileContents(
-          '/' + TESTING_TIRAMISU_FILE_NAME);
+          `/${TESTING_TIRAMISU_FILE_NAME}`);
       chrome.test.assertEq(
           TESTING_TEXT_TO_WRITE + TESTING_TEXT_TO_WRITE, newContents);
       chrome.test.succeed();
@@ -119,7 +116,6 @@
     async function replaceFileSuccess() {
       const fileEntry = await fileSystem.getFileEntry(
           TESTING_TIRAMISU_FILE_NAME, {create: false, exclusive: false});
-
       const fileWriter = await createWriter(fileEntry);
       fileWriter.seek(TESTING_TEXT_TO_WRITE.indexOf('creams'));
       await writeTextToFile(fileWriter, 'skates');
@@ -128,7 +124,7 @@
           TESTING_TEXT_TO_WRITE.replace('creams', 'skates') +
           TESTING_TEXT_TO_WRITE;
       const newContents = await remoteProvider.getFileContents(
-          '/' + TESTING_TIRAMISU_FILE_NAME);
+          `/${TESTING_TIRAMISU_FILE_NAME}`);
       chrome.test.assertEq(expectedContents, newContents);
       chrome.test.succeed();
     },
@@ -138,13 +134,13 @@
       const fileEntry = await fileSystem.getFileEntry(
           TestFileSystemProvider.FILE_FAIL, {create: false, exclusive: false});
       const fileWriter = await createWriter(fileEntry);
-      try {
-        await writeTextToFile(fileWriter, 'A lot of flowers.');
-        chrome.test.fail('Unexpectedly succeeded to write to a broken file.');
-      } catch (e) {
-        chrome.test.assertEq('InvalidStateError', e.name);
-        chrome.test.succeed();
-      }
+      const error =
+          await catchError(writeTextToFile(fileWriter, 'A lot of flowers.'));
+
+      chrome.test.assertTrue(
+          !!error, 'Unexpectedly succeeded to write to a broken file.');
+      chrome.test.assertEq('InvalidStateError', error.name);
+      chrome.test.succeed();
     },
 
     // Abort writing to a valid file with a registered abort handler. Should
@@ -157,7 +153,6 @@
           TestFileSystemProvider.FILE_BLOCKS_FOREVER,
           {create: false, exclusive: false});
       const fileWriter = await createWriter(fileEntry);
-
       // Start a write request, wait for it to reach the provider.
       writePromise = writeTextToFile(fileWriter, 'A lot of cherries.');
       await remoteProvider.waitForEvent('onWriteFileRequested');
@@ -165,14 +160,12 @@
       // Abort the operation after it's started.
       fileWriter.abort();
       await remoteProvider.waitForEvent('onAbortRequested');
+      const error = await catchError(writePromise);
 
-      try {
-        await writePromise;
-        chrome.test.fail('Unexpectedly finished writing, despite aborting.');
-      } catch (e) {
-        chrome.test.assertEq('AbortError', e.name);
-        chrome.test.succeed();
-      }
+      chrome.test.assertTrue(
+          !!error, 'Unexpectedly finished writing, despite aborting.');
+      chrome.test.assertEq('AbortError', error.name);
+      chrome.test.succeed();
     }
   ]);
 }
diff --git a/chrome/test/data/webui/chromeos/diagnostics/touchscreen_tester_test.js b/chrome/test/data/webui/chromeos/diagnostics/touchscreen_tester_test.js
index 9ab8a1c..99dbcb31b 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/touchscreen_tester_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/touchscreen_tester_test.js
@@ -84,7 +84,7 @@
         mockController.createFunctionMock(drawingProvider, 'drawTrail');
 
     const expectedTouches = new Map();
-    const touchStartEvents = [
+    const mockTouchEvents = [
       {
         id: 1,
         point: {x: 100, y: 150},
@@ -97,7 +97,7 @@
       },
     ];
 
-    for (const {id, point, pressure} of touchStartEvents) {
+    for (const {id, point, pressure} of mockTouchEvents) {
       // Add expected function call signature.
       mockDrawTrailMark.addExpectation(point.x, point.y);
       mockDrawTrail.addExpectation(
@@ -121,7 +121,7 @@
         mockController.createFunctionMock(drawingProvider, 'drawTrail');
 
     const expectedTouches = new Map();
-    const touchStartEvents = [
+    const mockTouchEvents = [
       {
         type: 'touchstart',
         id: 1,
@@ -136,7 +136,7 @@
       },
     ];
 
-    for (const {type, id, point, pressure} of touchStartEvents) {
+    for (const {type, id, point, pressure} of mockTouchEvents) {
       if (type === 'touchstart') {
         mockDrawTrail.addExpectation(
             point.x - 1, point.y, point.x, point.y, pressure);
@@ -153,4 +153,62 @@
       mockController.verifyMocks();
     }
   });
+
+  test('OnDrawEnd', async () => {
+    await initializeTouchscreenTester();
+    await openTester();
+
+    // Mock drawTrailMark and drawTrail function.
+    const drawingProvider = touchscreenTesterElement.getDrawingProvider();
+    const mockController = new MockController();
+    const mockDrawTrailMark =
+        mockController.createFunctionMock(drawingProvider, 'drawTrailMark');
+    const mockDrawTrail =
+        mockController.createFunctionMock(drawingProvider, 'drawTrail');
+
+    const expectedTouches = new Map();
+    const mockTouchEvents = [
+      {
+        type: 'touchstart',
+        id: 1,
+        point: {x: 100, y: 150},
+        pressure: 40,
+      },
+      {
+        type: 'touchmove',
+        id: 1,
+        point: {x: 110, y: 160},
+        pressure: 30,
+      },
+      {
+        type: 'touchend',
+        id: 1,
+        point: {x: 110, y: 160},
+        pressure: 30,
+      },
+    ];
+
+    for (const {type, id, point, pressure} of mockTouchEvents) {
+      if (type === 'touchstart') {
+        mockDrawTrailMark.addExpectation(point.x, point.y);
+        mockDrawTrail.addExpectation(
+            point.x - 1, point.y, point.x, point.y, pressure);
+        touchscreenTesterElement.onDrawStart(id, point, pressure);
+        expectedTouches.set(id, point);
+      } else if (type === 'touchmove') {
+        const previousPt = expectedTouches.get(id);
+        mockDrawTrail.addExpectation(
+            previousPt.x, previousPt.y, point.x, point.y, pressure);
+        touchscreenTesterElement.onDraw(id, point, pressure);
+        expectedTouches.set(id, point);
+      } else if (type === 'touchend') {
+        mockDrawTrailMark.addExpectation(point.x, point.y);
+        touchscreenTesterElement.onDrawEnd(id, point);
+        expectedTouches.delete(id);
+      }
+
+      assertDeepEquals(expectedTouches, touchscreenTesterElement.getTouches());
+      mockController.verifyMocks();
+    }
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/parent_access/OWNERS b/chrome/test/data/webui/chromeos/parent_access/OWNERS
index eb1b58bc..43ddb31 100644
--- a/chrome/test/data/webui/chromeos/parent_access/OWNERS
+++ b/chrome/test/data/webui/chromeos/parent_access/OWNERS
@@ -1 +1 @@
-file://chrome/browser/ui/webui/chromeos/parent_access/OWNERS
+file://chrome/browser/ui/webui/ash/parent_access/OWNERS
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js
index 8b3070c..794867c 100644
--- a/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js
+++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_browsertest.js
@@ -6,7 +6,7 @@
 
 GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
-GEN('#include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h"');
+GEN('#include "chrome/browser/ui/webui/ash/parent_access/parent_access_browsertest_base.h"');
 GEN('#include "content/public/test/browser_test.h"');
 
 
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_platform_grpc.cc b/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_platform_grpc.cc
index c40f874..7508946c 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_platform_grpc.cc
+++ b/chromecast/cast_core/runtime/browser/runtime_application_dispatcher_platform_grpc.cc
@@ -124,13 +124,16 @@
   if (metrics_recorder_service_) {
     metrics_recorder_service_->OnCloseSoon(base::DoNothing());
     metrics_recorder_service_.reset();
+    metrics_recorder_stub_.reset();
+    action_recorder_.reset();
   }
 
   if (grpc_server_) {
     grpc_server_->Stop();
     grpc_server_.reset();
-    LOG(INFO) << "Runtime service stopped";
   }
+
+  LOG(INFO) << "Runtime service stopped";
 }
 
 std::unique_ptr<CastEventBuilder>
@@ -263,16 +266,17 @@
     cast::runtime::RuntimeServiceHandler::StartMetricsRecorder::Reactor*
         reactor) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!action_recorder_);
+  DCHECK(!metrics_recorder_stub_);
+  DCHECK(!metrics_recorder_service_);
+
   if (request.metrics_recorder_service_info().grpc_endpoint().empty()) {
     reactor->Write(grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
                                 "MetricsRecord service endpoint is missing"));
     return;
   }
 
-  if (!action_recorder_) {
-    action_recorder_.emplace();
-  }
-
+  action_recorder_.emplace();
   metrics_recorder_stub_.emplace(
       request.metrics_recorder_service_info().grpc_endpoint());
   metrics_recorder_service_.emplace(
@@ -291,6 +295,14 @@
     cast::runtime::RuntimeServiceHandler::StopMetricsRecorder::Reactor*
         reactor) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!metrics_recorder_service_) {
+    LOG(ERROR) << "Droping metrics recorging stop request as service is not "
+                  "available anymore";
+    reactor->Write(grpc::Status(grpc::StatusCode::UNAVAILABLE,
+                                "Metrics recording service is not available"));
+    return;
+  }
+
   metrics_recorder_service_->OnCloseSoon(
       base::BindOnce(&RuntimeApplicationDispatcherPlatformGrpc::
                          OnMetricsRecorderServiceStopped,
@@ -406,6 +418,9 @@
         reactor) {
   DVLOG(2) << "MetricsRecorderService stopped";
   metrics_recorder_service_.reset();
+  metrics_recorder_stub_.reset();
+  action_recorder_.reset();
+
   reactor->Write(cast::runtime::StopMetricsRecorderResponse());
 }
 
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler.h b/chromeos/ash/components/audio/audio_devices_pref_handler.h
index 9e2f4896..fc54afdd 100644
--- a/chromeos/ash/components/audio/audio_devices_pref_handler.h
+++ b/chromeos/ash/components/audio/audio_devices_pref_handler.h
@@ -19,10 +19,9 @@
 class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) AudioDevicesPrefHandler
     : public base::RefCountedThreadSafe<AudioDevicesPrefHandler> {
  public:
-  // Integer because C++ does not allow static const double in header files.
-  static const int kDefaultInputGainPercent = 50;
-  static const int kDefaultOutputVolumePercent = 75;
-  static const int kDefaultHdmiOutputVolumePercent = 100;
+  static constexpr double kDefaultInputGainPercent = 50;
+  static constexpr double kDefaultOutputVolumePercent = 75;
+  static constexpr double kDefaultHdmiOutputVolumePercent = 100;
 
   // Gets the audio output volume value from prefs for a device. Since we can
   // only have either a gain or a volume for a device (depending on whether it
@@ -85,7 +84,7 @@
       size_t keep_devices) = 0;
 
  protected:
-  virtual ~AudioDevicesPrefHandler() {}
+  virtual ~AudioDevicesPrefHandler() = default;
 
  private:
   friend class base::RefCountedThreadSafe<AudioDevicesPrefHandler>;
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 745678d2..70a8576 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -209,16 +209,6 @@
   # https://crbug.com/1346149
   "apps.Sharesheet",
 
-  # http://crbug.com/1350529
-  "crostini.RemoveCancel.bullseye_stable",
-  "crostini.RemoveCancel.buster_stable",
-  "crostini.RemoveOk.bullseye_stable",
-  "crostini.RemoveOk.buster_stable",
-
-  # http://crbug.com/1259466
-  "crostini.ShareMovies.bullseye_stable_gaia",
-  "crostini.ShareMovies.buster_stable_gaia",
-
   # b/241122961
   "policy.BlockThirdPartyCookies",
 
@@ -325,7 +315,6 @@
   "inputs.VirtualKeyboardChangeInput.a11y",
 
   # https://crbug.com/1373329
-  "lockscreen.CloseLid",
   "peripherals.LaunchAppFromSettings.scan",
   "policy.AllowedLanguages",
   "terminal.Crosh",
diff --git a/components/exo/ui_lock_controller.cc b/components/exo/ui_lock_controller.cc
index c8620e6..eb90e23 100644
--- a/components/exo/ui_lock_controller.cc
+++ b/components/exo/ui_lock_controller.cc
@@ -460,9 +460,6 @@
 ExitNotifier* GetExitNotifier(UILockController* controller,
                               aura::Window* window,
                               bool create) {
-  if (!base::FeatureList::IsEnabled(chromeos::features::kExoLockNotification))
-    return nullptr;
-
   if (!window)
     return nullptr;
 
diff --git a/components/exo/ui_lock_controller_unittest.cc b/components/exo/ui_lock_controller_unittest.cc
index ca008db..7557d14 100644
--- a/components/exo/ui_lock_controller_unittest.cc
+++ b/components/exo/ui_lock_controller_unittest.cc
@@ -138,8 +138,6 @@
   void SetUp() override {
     test::ExoTestBase::SetUp();
     seat_ = std::make_unique<Seat>();
-    scoped_feature_list_.InitWithFeatures(
-        {chromeos::features::kExoLockNotification}, {});
     WMHelper::GetInstance()->RegisterAppPropertyResolver(
         std::make_unique<TestPropertyResolver>());
   }
diff --git a/components/feed/core/v2/metrics_reporter.cc b/components/feed/core/v2/metrics_reporter.cc
index 37dbbae..3d00fda 100644
--- a/components/feed/core/v2/metrics_reporter.cc
+++ b/components/feed/core/v2/metrics_reporter.cc
@@ -731,6 +731,7 @@
     case FeedUserActionType::kFollowRecommendationIPHShown:
     case FeedUserActionType::kFollowingFeedSelectedGroupByPublisher:
     case FeedUserActionType::kFollowingFeedSelectedSortByLatest:
+    case FeedUserActionType::kTappedGotItFeedPostFollowActiveHelp:
       // Nothing additional for these actions. Note that some of these are iOS
       // only.
 
diff --git a/components/feed/core/v2/public/common_enums.cc b/components/feed/core/v2/public/common_enums.cc
index 25bd0795..a7f41b5 100644
--- a/components/feed/core/v2/public/common_enums.cc
+++ b/components/feed/core/v2/public/common_enums.cc
@@ -132,6 +132,8 @@
       return out << "kFollowingFeedSelectedGroupByPublisher";
     case FeedUserActionType::kFollowingFeedSelectedSortByLatest:
       return out << "kFollowingFeedSelectedSortByLatest";
+    case FeedUserActionType::kTappedGotItFeedPostFollowActiveHelp:
+      return out << "kTappedGotItFeedPostFollowActiveHelp";
   }
 }
 
diff --git a/components/feed/core/v2/public/common_enums.h b/components/feed/core/v2/public/common_enums.h
index 552274a..6980870 100644
--- a/components/feed/core/v2/public/common_enums.h
+++ b/components/feed/core/v2/public/common_enums.h
@@ -107,8 +107,8 @@
   kTappedRefollowAfterUnfollowOnSnackbar = 35,
   // User tapped to unfollow using the snackbar 'try again' option.
   kTappedUnfollowTryAgainOnSnackbar = 36,
-  // After following an active web feed, the user tapped to go to feed using the
-  // post-follow help dialog.
+  // After following an active web feed, the user tapped to go to Following feed
+  // using the post-follow help dialog.
   kTappedGoToFeedPostFollowActiveHelp = 37,
   // After following an active web feed, the user tapped to dismiss the
   // post-follow help dialog.
@@ -165,7 +165,10 @@
   kFollowingFeedSelectedGroupByPublisher = 59,
   // User selected the "Sort by Latest" Following feed sort type.
   kFollowingFeedSelectedSortByLatest = 60,
-  kMaxValue = kFollowingFeedSelectedSortByLatest,
+  // After following an active web feed, the user tapped on 'got it' to
+  // close the post-follow help dialog.
+  kTappedGotItFeedPostFollowActiveHelp = 61,
+  kMaxValue = kTappedGotItFeedPostFollowActiveHelp,
 };
 
 // For testing and debugging only.
diff --git a/components/navigation_metrics/navigation_metrics.cc b/components/navigation_metrics/navigation_metrics.cc
index eab6d21..4f666d3 100644
--- a/components/navigation_metrics/navigation_metrics.cc
+++ b/components/navigation_metrics/navigation_metrics.cc
@@ -81,12 +81,21 @@
   size_t label_count =
       base::ranges::count(etld_plus_one.begin(), etld_plus_one.end(), '.') + 1;
 
+  // Replace non-standard separators with "." (U002E). Sometimes users may input
+  // non-standard separators, causing issues when splitting labels based on ".".
+  // This follows the Unicode IDNA spec:
+  // https://www.unicode.org/reports/tr46/#TableDerivationStep1
+  std::u16string separator_replaced_hostname;
+  base::ReplaceChars(hostname16, u"\uff0e\u3002\uff61", u".",
+                     &separator_replaced_hostname);
+
   // Keeping empty labels is necessary if there is a trailing dot, to make sure
   // `label_count` matches the `labels16` vector. See crbug.com/1362507.
-  std::vector<std::u16string> labels16 = base::SplitString(
-      hostname16, u".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+  std::vector<std::u16string> labels16 =
+      base::SplitString(separator_replaced_hostname, u".",
+                        base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+  CHECK_LE(label_count, labels16.size());
   size_t extra_label_count = labels16.size() - label_count;
-  DCHECK_LE(extra_label_count, labels16.size());
   labels16.erase(labels16.begin(), labels16.begin() + extra_label_count);
   return base::JoinString(labels16, u".");
 }
diff --git a/components/navigation_metrics/navigation_metrics_unittest.cc b/components/navigation_metrics/navigation_metrics_unittest.cc
index 94b9aad1..095581f 100644
--- a/components/navigation_metrics/navigation_metrics_unittest.cc
+++ b/components/navigation_metrics/navigation_metrics_unittest.cc
@@ -164,6 +164,17 @@
   histograms.ExpectTotalCount(kHistogram, 4);
   histograms.ExpectBucketCount(kHistogram, false, 2);
   histograms.ExpectBucketCount(kHistogram, true, 2);
+
+  // Should work well with non-standard separators
+  RecordIDNA2008Metrics(u"example。com");
+  histograms.ExpectTotalCount(kHistogram, 5);
+  histograms.ExpectBucketCount(kHistogram, false, 3);
+  histograms.ExpectBucketCount(kHistogram, true, 2);
+
+  RecordIDNA2008Metrics(u"subdomain。faß。de");
+  histograms.ExpectTotalCount(kHistogram, 6);
+  histograms.ExpectBucketCount(kHistogram, false, 3);
+  histograms.ExpectBucketCount(kHistogram, true, 3);
 }
 
 // Regression test for crbug.com/1362507. Tests that the IDNA2008 metrics code
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc
index 55c2e2c9..be24bc4c 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -240,8 +240,8 @@
         /*input_text=*/base::ASCIIToUTF16(std::string()));
 
     // If the appropriate header text and section are not provided by the server
-    // the default omnibox::SECTION_LOCAL_HISTORY_ZPS will be used and the local
-    // history zero-prefix suggestions will be shown without a header.
+    // the default omnibox::SECTION_PERSONALIZED_ZERO_SUGGEST will be used and
+    // the local history zero-prefix suggestions will be shown without a header.
     suggestion.set_suggestion_group_id(
         omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST);
 
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index 802c98c..5011b72 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -856,12 +856,13 @@
   // Keeps the mapping from server-provided group IDs to those known to Chrome.
   std::unordered_map<omnibox::GroupId, omnibox::GroupId> chrome_group_ids_map;
 
-  // Adds the group information for the given suggestion group ID and config.
-  auto add_group_info = [&](const omnibox::GroupId suggestion_group_id,
-                            const omnibox::GroupConfig& group_config) {
-    // The group information is already stored if the group ID is seen before.
+  // Adds the given group config to the results for the given group ID. Returns
+  // true if the entry was added to or was already present in the results.
+  auto add_group_config = [&](const omnibox::GroupId suggestion_group_id,
+                              const omnibox::GroupConfig& group_config) {
+    // The group config is already added if the group ID was seen before.
     if (base::Contains(chrome_group_ids_map, suggestion_group_id)) {
-      return;
+      return true;
     }
 
     // Assign a 0-based index to the group based on the number of groups so far.
@@ -877,19 +878,25 @@
                                      : ChromeGroupIdForRemoteGroupIdAndIndex(
                                            suggestion_group_id, group_index);
 
-    // Do not propagate the server-provided group IDs if Chrome ran out of
-    // group IDs to assign or if the group ID was invalid to begin with.
+    // Do not add the group config if Chrome ran out of group IDs to assign or
+    // if the group ID was invalid to begin with.
     if (chrome_group_id == omnibox::GROUP_INVALID) {
-      return;
+      return false;
     }
 
     // Remember the conversion.
     chrome_group_ids_map[suggestion_group_id] = chrome_group_id;
 
-    // Store the associated suggestion group information in the results.
+    // There is nothing to do if the group config has been added before.
+    if (base::Contains(results->suggestion_groups_map, chrome_group_id)) {
+      return true;
+    }
+
+    // Store the group config with the appropriate section in the results.
     results->suggestion_groups_map[chrome_group_id].MergeFrom(group_config);
     results->suggestion_groups_map[chrome_group_id].set_section(
         ChromeGroupSectionForRemoteGroupIndex(group_index));
+    return true;
   };
 
   for (auto& suggest_result : results->suggest_results) {
@@ -900,29 +907,25 @@
     const omnibox::GroupId suggestion_group_id =
         suggest_result.suggestion_group_id().value();
 
-    // Do not propagate the server-provided group IDs without any associated
-    // group config.
-    if (!base::Contains(groups_info.group_configs(), suggestion_group_id)) {
-      suggest_result.set_suggestion_group_id(absl::nullopt);
+    // Add the group config associated with the suggestion, if the suggestion
+    // has a valid group ID and a corresponding group config is found in the
+    // response. Note that a group ID is deemed invalid if Chrome runs out of
+    // group IDs to assign or if the group ID was invalid to begin with.
+    if (!base::Contains(groups_info.group_configs(), suggestion_group_id) ||
+        !add_group_config(suggestion_group_id, groups_info.group_configs().at(
+                                                   suggestion_group_id))) {
       continue;
     }
 
-    // Add the group information for the group associated with the suggestion.
-    add_group_info(suggestion_group_id,
-                   groups_info.group_configs().at(suggestion_group_id));
-
-    // Update or reset the group ID in the suggestion.
-    if (base::Contains(chrome_group_ids_map, suggestion_group_id)) {
-      suggest_result.set_suggestion_group_id(
-          chrome_group_ids_map[suggestion_group_id]);
-    } else {
-      suggest_result.set_suggestion_group_id(absl::nullopt);
-    }
+    // Update the group ID in the suggestion.
+    suggest_result.set_suggestion_group_id(
+        chrome_group_ids_map[suggestion_group_id]);
   }
 
-  // Add the group information for the remaining groups without any suggestions.
+  // Add the remaining group configs without any suggestions in the response.
   // The only known use case is the personalized zero-suggest which is also
-  // produced by Chrome and relies on the server-provided group config to show.
+  // produced by Chrome and relies on the server-provided group config to show
+  // with the appropriate header text, where a header text is applicable.
   for (const auto& entry : groups_info.group_configs()) {
     // Do not use omnibox::GroupIdForNumber() because |groups_info| keys may not
     // be present in omnibox::GroupId. However, casting int values into
@@ -930,7 +933,7 @@
     // as omnibox::GroupId enum has a fixed int underlying type.
     // TODO(crbug.com/1343512): Use omnibox::GroupIdForNumber() once the server
     //  response migrates to a serialized omnibox::GroupsInfo proto.
-    add_group_info(static_cast<omnibox::GroupId>(entry.first), entry.second);
+    add_group_config(static_cast<omnibox::GroupId>(entry.first), entry.second);
   }
 
   return true;
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index 136cc8d5..3fd61470 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -433,8 +433,8 @@
               *results.suggest_results[2].suggestion_group_id());
 
     ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion());
-    // This suggestion belongs to an unrecognized group.
-    ASSERT_EQ(absl::nullopt, results.suggest_results[3].suggestion_group_id());
+    ASSERT_EQ(static_cast<omnibox::GroupId>(40009),
+              results.suggest_results[3].suggestion_group_id());
   }
   {
     std::string json_data = R"([
@@ -523,8 +523,8 @@
               *results.suggest_results[2].suggestion_group_id());
 
     ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion());
-    // This suggestion belongs to an unrecognized group.
-    ASSERT_EQ(absl::nullopt, results.suggest_results[3].suggestion_group_id());
+    ASSERT_EQ(static_cast<omnibox::GroupId>(40009),
+              results.suggest_results[3].suggestion_group_id());
   }
   {
     std::string json_data = R"([
@@ -625,8 +625,8 @@
               *results.suggest_results[2].suggestion_group_id());
 
     ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion());
-    // This suggestion belongs to an unrecognized group.
-    ASSERT_EQ(absl::nullopt, results.suggest_results[3].suggestion_group_id());
+    ASSERT_EQ(static_cast<omnibox::GroupId>(40000),
+              results.suggest_results[3].suggestion_group_id());
   }
   {
     std::string json_data = R"([
@@ -735,7 +735,8 @@
               *results.suggest_results[4].suggestion_group_id());
     ASSERT_EQ(omnibox::GROUP_VISITED_DOC_RELATED,
               *results.suggest_results[5].suggestion_group_id());
-    ASSERT_EQ(absl::nullopt, results.suggest_results[6].suggestion_group_id());
+    ASSERT_EQ(static_cast<omnibox::GroupId>(40006),
+              results.suggest_results[6].suggestion_group_id());
   }
 }
 
@@ -830,8 +831,8 @@
               *results.suggest_results[2].suggestion_group_id());
 
     ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion());
-    // This suggestion belongs to an unrecognized group.
-    ASSERT_EQ(absl::nullopt, results.suggest_results[3].suggestion_group_id());
+    ASSERT_EQ(static_cast<omnibox::GroupId>(40000),
+              results.suggest_results[3].suggestion_group_id());
   }
   {
     omnibox::GroupsInfo groups_info;
diff --git a/components/omnibox/browser/suggestion_group_util.cc b/components/omnibox/browser/suggestion_group_util.cc
index d9dee60..f4a8a19 100644
--- a/components/omnibox/browser/suggestion_group_util.cc
+++ b/components/omnibox/browser/suggestion_group_util.cc
@@ -17,7 +17,7 @@
   groups[omnibox::GROUP_MOBILE_CLIPBOARD].set_section(
       omnibox::SECTION_MOBILE_CLIPBOARD);
   groups[omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST].set_section(
-      omnibox::SECTION_LOCAL_HISTORY_ZPS);
+      omnibox::SECTION_PERSONALIZED_ZERO_SUGGEST);
   return groups;
 }
 
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc
index 140083db..685331a 100644
--- a/components/viz/common/quads/compositor_render_pass_unittest.cc
+++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -61,7 +61,7 @@
   AggregatedRenderPassId render_pass_id{3u};
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
-      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateOpacityFilter(0.5));
@@ -123,7 +123,7 @@
   CompositorRenderPassId id{3};
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
-      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateOpacityFilter(0.5));
@@ -181,7 +181,7 @@
   CompositorRenderPassId contrib_id{4};
   gfx::Rect contrib_output_rect(10, 15, 12, 17);
   gfx::Transform contrib_transform_to_root =
-      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect contrib_damage_rect(11, 16, 10, 15);
   cc::FilterOperations contrib_filters;
   contrib_filters.Append(cc::FilterOperation::CreateSepiaFilter(0.5));
@@ -239,7 +239,7 @@
   CompositorRenderPassId id{3};
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
-      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateOpacityFilter(0.5));
diff --git a/components/viz/common/quads/draw_quad_perftest.cc b/components/viz/common/quads/draw_quad_perftest.cc
index d1459bbf..3e8c197 100644
--- a/components/viz/common/quads/draw_quad_perftest.cc
+++ b/components/viz/common/quads/draw_quad_perftest.cc
@@ -37,7 +37,7 @@
 
 SharedQuadState* CreateSharedQuadState(CompositorRenderPass* render_pass) {
   gfx::Transform quad_transform =
-      gfx::Transform::AffineForTesting(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.0, 1.0, 0.5, 0.0);
   gfx::Rect content_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
   bool are_contents_opaque = false;
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index cf18d47..1b7ceef 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -48,7 +48,7 @@
 
 TEST(DrawQuadTest, CopySharedQuadState) {
   gfx::Transform quad_transform =
-      gfx::Transform::AffineForTesting(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.0, 1.0, 0.5, 0.0);
   gfx::Rect layer_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
   gfx::Rect clip_rect(19, 21, 23, 25);
@@ -73,7 +73,7 @@
 
 SharedQuadState* CreateSharedQuadState(CompositorRenderPass* render_pass) {
   gfx::Transform quad_transform =
-      gfx::Transform::AffineForTesting(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.0, 1.0, 0.5, 0.0);
   gfx::Rect layer_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
   bool are_contents_opaque = true;
diff --git a/content/browser/back_forward_cache_internal_browsertest.cc b/content/browser/back_forward_cache_internal_browsertest.cc
index c796909a..59ced44 100644
--- a/content/browser/back_forward_cache_internal_browsertest.cc
+++ b/content/browser/back_forward_cache_internal_browsertest.cc
@@ -262,7 +262,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
-                       DISABLED_NavigationsAreFullyCommitted) {
+                       NavigationsAreFullyCommitted) {
+  // Sometimes messages arrive from a renderer to browser for the page in
+  // back/forward cache (message on content.mojom.FrameHost), because the input
+  // task queue is currently not frozen. Do not fail for unexpected messages.
+  DoNotFailForUnexpectedMessagesWhileCached();
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // During a navigation, the document being navigated *away from* can either be
diff --git a/content/browser/find_request_manager.cc b/content/browser/find_request_manager.cc
index 8ecdc8a9..dba94e34 100644
--- a/content/browser/find_request_manager.cc
+++ b/content/browser/find_request_manager.cc
@@ -383,6 +383,8 @@
   find_request_queue_.emplace(request_id, search_text, std::move(options));
   if (find_request_queue_.size() == 1)
     FindInternal(find_request_queue_.front());
+  if (request_id == current_session_id_)
+    find_request_queue_.pop();
 }
 
 void FindRequestManager::ForEachAddedFindInPageRenderFrameHost(
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc
index 2d3a3361..6b0f30e 100644
--- a/content/browser/find_request_manager_browsertest.cc
+++ b/content/browser/find_request_manager_browsertest.cc
@@ -1476,6 +1476,10 @@
   auto options = blink::mojom::FindOptions::New();
   options->run_synchronously_for_testing = true;
   Find("result", options.Clone());
+  // Initial find request is pop from the queue immediately so we make a second
+  // find request.
+  options->new_session = false;
+  Find("result", options.Clone());
 
   // Create a fenced frame.
   GURL find_test_url =
diff --git a/content/browser/renderer_host/pending_beacon_host.cc b/content/browser/renderer_host/pending_beacon_host.cc
index 07e0c44..cb13820 100644
--- a/content/browser/renderer_host/pending_beacon_host.cc
+++ b/content/browser/renderer_host/pending_beacon_host.cc
@@ -78,7 +78,7 @@
 
   // Checks if it has Background Sync granted before sending out the rest of
   // beacons.
-  // https://github.com/WICG/unload-beacon#privacy
+  // https://github.com/WICG/pending-beacon#privacy
   if (IsBackgroundSyncGranted(&render_frame_host())) {
     Send(beacons_);
   }
@@ -131,7 +131,7 @@
   // after users think they have left a page, beacons queued in that page
   // still exist and get sent through the new network, which leaks navigation
   // history to the new network.
-  // See https://github.com/WICG/unload-beacon/issues/30.
+  // See https://github.com/WICG/pending-beacon/issues/30.
 
   // Swaps out from private field first to make any potential subsequent send
   // requests from renderer no-ops.
diff --git a/content/browser/renderer_host/pending_beacon_host.h b/content/browser/renderer_host/pending_beacon_host.h
index 928fa0e..288daca6 100644
--- a/content/browser/renderer_host/pending_beacon_host.h
+++ b/content/browser/renderer_host/pending_beacon_host.h
@@ -98,7 +98,7 @@
   // "Unlike `SendBeacon()` which is triggered by the renderer, this method is
   // called only by the browser process itself.
   //
-  // https://github.com/WICG/unload-beacon/issues/30
+  // https://github.com/WICG/pending-beacon/issues/30
   void SendAllOnNavigation();
 
   // `RenderProcessHostObserver` implementation.
@@ -146,7 +146,7 @@
   // Browser-side pending beacon constructor. Parameters correspond to the
   // renderer-side PendingBeacon class.
   // API explainer can be found at:
-  // https://github.com/WICG/unload-beacon/blob/main/README.md
+  // https://github.com/WICG/pending-beacon/blob/main/README.md
   Beacon(const GURL& url,
          blink::mojom::BeaconMethod method,
          PendingBeaconHost* beacon_host,
diff --git a/docs/experiments/page-unload-beacon.md b/docs/experiments/page-unload-beacon.md
index 851ebd04..42011dd 100644
--- a/docs/experiments/page-unload-beacon.md
+++ b/docs/experiments/page-unload-beacon.md
@@ -1,105 +1 @@
-# Page Unload Beacon API
-
-- Contact: pending-beacon-experiment@chromium.org
-- API Feedback: https://github.com/WICG/unload-beacon/issues
-
-This document describes the status of the current implementation of the
-[**Page Unload Beacon API**](https://wicg.github.io/unload-beacon/)
-(a.k.a. PendingBeacon API) in Chrome, and how to enable it manually.
-
-Starting from [version 107][status], Chrome experimentally supports the
-Page Unload Beacon API,
-which allows website authors to specify one or more beacons (HTTP requests) that should be sent reliably when the page is being unloaded.
-
-See the [public explainer](https://github.com/WICG/unload-beacon#readme) to learn more about how it works.
-
-Note that this API is not enabled by default. Instead, Chrome plans to run A/B testing to evaluate its impact. But Chrome also provides some ways to fully opt-in to the API for web developers who what to try the features.
-
-## What’s supported
-
-Chrome supports all the JavaScript APIs described in the explainer, specifically:
-
-- [`class PendingPostBeacon`](https://github.com/WICG/unload-beacon#pendingpostbeacon)
-- [`class PendingGetBeacon`](https://github.com/WICG/unload-beacon#pendinggetbeacon)
-- and all of the properties and methods described in [PendingBeacon](https://github.com/WICG/unload-beacon#pendingbeacon), with some behaviors not supported.
-
-## What’s not supported
-
-The following features are not yet supported in Chrome:
-
-- When calling `setData(data)` on a `PendingPostBeacon`, the `data` payload cannot be
-  - A complex body, i.e. for a `data` of `FormData` type, it can only have single [entry/part][formdata-entry].
-  - A streaming body, i.e. a `data` cannot be a [ReadableStream].
-- Crash recovery related behaviors and privacy requirements: not yet supported. Chrome currently doesn't store any PendingBeacon on disk.
-- Delete pending beacons for a site if a user clears site data: not supported yet, as crash recovery from disk is not yet supported.
-- Beacon requests are not yet observable in Chrome DevTools.
-
-The following features work differently than the one described in explainer:
-
-- Beacons must not leak navigation history to the network provider that it should not know:
-  supported by forcing to send out all beacons on navigating away from a document.
-- The beacon destination URL should be modifiable: only `PendingGetBeacon` can
-  update its URL via `setURL()` method.
-- Beacon Sending behavior: the Chrome implementation currently queues all
-  created instances of Pending*Beacon for sending. But in the explainer, it
-  specifies that a `PendingPostBeacon` is only queued if it has non-undefined
-  and non-null data (described in `setData()` method).
-- Beacons must be sent over HTTPS: current implementation doesn't enforce HTTPS,
-  which means if web developer creates a `Pending*Beacon` with HTTP URL property, it will still work.
-- Beacons max TTL is bound by Chrome's back/forward cache TTL, which is currently 3 minutes.
-
-[formdata-entry]: https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries
-[ReadableStream]: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
-
-## Activation
-
-The API can be enabled by a command line flag, or via
-[Origin Trial](https://developer.chrome.com/blog/origin-trials/).
-
-### Using command line flag
-
-Passing the `--enable-features=PendingBeaconAPI` command line flag
-to Chrome enables PendingBeacon API support.
-
-### Using Origin Trial
-
-[**Trial for Page Unload Beacon**](https://developer.chrome.com/origintrials/#/view_trial/1581889369113886721).
-
-You can opt any page on your origin into PendingBeacon API Origin Trial by [requesting a token][ot-tutorial] for your origin via the above link. Include the token in your page so that Chrome can recognize your page is opted in.
-
-The simplest way is to include the following line in your page:
-
-```html
-<meta http-equiv="origin-trial" content="**your token**">
-```
-
-Or you can also include the token in your HTTP request.
-
-*** note
-**NOTE**: Even with the Origin Trial token, **NOT all of visits to your page** will have the API enabled. It is because as mentioned above, Chrome plan to do A/B testing on the API.
-***
-
-[ot-tutorial]: https://developer.chrome.com/docs/web-platform/origin-trials/#take-part-in-an-origin-trial
-
-### Verifying the API is working
-
-Added the following line to a web page, and load the page into a Chrome tab.
-
-```html
-<script>
-new PendingGetBeacon('/test');
-</script>
-```
-
-Close the tab, and you should be able to observe a request sent to `/test` on
-your web server that hosts the page.
-
-## Related Links
-
-- [Chrome Platform Status - Feature: Declarative PendingBeacon API][status]
-- [Page Unload Beacon Explainer on GitHub](https://github.com/WICG/unload-beacon#readme)
-- [Page Unload Beacon API Spec (draft)](https://wicg.github.io/unload-beacon/)
-- Ask questions about API & Spec via [new issue](https://github.com/WICG/unload-beacon/issues/new)
-- [Page Unload Beacon Design Doc in Chromium](https://docs.google.com/document/d/1QIFUu6Ne8x0W62RKJSoTtZjSd_bIM2yXZSELxdeuTFo/edit#)
-
-[status]: https://chromestatus.com/feature/5690553554436096
+This page has been moved to [Pending Beacon API](pending-beacon.md).
diff --git a/docs/experiments/pending-beacon.md b/docs/experiments/pending-beacon.md
new file mode 100644
index 0000000..0e33e070
--- /dev/null
+++ b/docs/experiments/pending-beacon.md
@@ -0,0 +1,124 @@
+# Pending Beacon API
+
+- Contact: pending-beacon-experiment@chromium.org
+- API Feedback: https://github.com/WICG/pending-beacon/issues
+
+This document describes the status of the current implementation of the
+[**Pending Beacon API**](https://wicg.github.io/pending-beacon/)
+(a.k.a. PendingBeacon API) in Chrome, and how to enable it manually.
+
+Starting from [version 107][status], Chrome experimentally supports the
+Pending Beacon API,
+which allows website authors to specify one or more beacons (HTTP requests) that
+should be sent reliably when the page is being unloaded.
+
+See the [public explainer](https://github.com/WICG/pending-beacon#readme) to
+learn more about how it works.
+
+Note that this API is not enabled by default. Instead, Chrome plans to run A/B
+testing to evaluate its impact. But Chrome also provides some ways to fully
+opt-in to the API for web developers who what to try the features.
+
+## What’s supported
+
+Chrome supports all the JavaScript APIs described in the explainer,
+specifically:
+
+- [`class PendingPostBeacon`](https://github.com/WICG/pending-beacon#pendingpostbeacon)
+- [`class PendingGetBeacon`](https://github.com/WICG/pending-beacon#pendinggetbeacon)
+- and all of the properties and methods described in
+  [PendingBeacon](https://github.com/WICG/pending-beacon#pendingbeacon), with
+  some behaviors not supported.
+
+## What’s not supported
+
+The following features are not yet supported in Chrome:
+
+- When calling `setData(data)` on a `PendingPostBeacon`, the `data` payload
+  cannot be
+  - A complex body, i.e. for a `data` of `FormData` type, it can only have
+    single [entry/part][formdata-entry].
+  - A streaming body, i.e. a `data` cannot be a [ReadableStream].
+- Crash recovery related behaviors and privacy requirements: not yet supported.
+  Chrome currently doesn't store any PendingBeacon on disk.
+- Delete pending beacons for a site if a user clears site data: not supported
+  yet, as crash recovery from disk is not yet supported.
+- Beacon requests are not yet observable in Chrome DevTools.
+
+The following features work differently than the one described in explainer:
+
+- Beacons must not leak navigation history to the network provider that it
+  should not know:
+  supported by forcing to send out all beacons on navigating away from a
+  document.
+- The beacon destination URL should be modifiable: only `PendingGetBeacon` can
+  update its URL via `setURL()` method.
+- Beacon Sending behavior: the Chrome implementation currently queues all
+  created instances of Pending*Beacon for sending. But in the explainer, it
+  specifies that a `PendingPostBeacon` is only queued if it has non-undefined
+  and non-null data (described in `setData()` method).
+- Beacons must be sent over HTTPS: current implementation doesn't enforce HTTPS,
+  which means if web developer creates a `Pending*Beacon` with HTTP URL
+  property, it will still work.
+- Beacons max TTL is bound by Chrome's back/forward cache TTL, which is
+  currently **10 minutes**.
+
+[formdata-entry]: https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries
+[ReadableStream]: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
+
+## Activation
+
+The API can be enabled by a command line flag, or via
+[Origin Trial](https://developer.chrome.com/blog/origin-trials/).
+
+### Using command line flag
+
+Passing the `--enable-features=PendingBeaconAPI` command line flag
+to Chrome enables PendingBeacon API support.
+
+### Using Origin Trial
+
+[**Trial for Pending Beacon**](https://developer.chrome.com/origintrials/#/view_trial/1581889369113886721).
+
+You can opt any page on your origin into PendingBeacon API Origin Trial by
+[requesting a token][ot-tutorial] for your origin via the above link. Include
+the token in your page so that Chrome can recognize your page is opted in.
+
+The simplest way is to include the following line in your page:
+
+```html
+<meta http-equiv="origin-trial" content="**your token**">
+```
+
+Or you can also include the token in your HTTP request.
+
+*** note
+**NOTE**: Even with the Origin Trial token, **NOT all of visits to your page**
+will have the API enabled. It is because as mentioned above, Chrome plan to do
+A/B testing on the API.
+***
+
+[ot-tutorial]: https://developer.chrome.com/docs/web-platform/origin-trials/#take-part-in-an-origin-trial
+
+### Verifying the API is working
+
+Added the following line to a web page, and load the page into a Chrome tab.
+
+```html
+<script>
+new PendingGetBeacon('/test');
+</script>
+```
+
+Close the tab, and you should be able to observe a request sent to `/test` on
+your web server that hosts the page.
+
+## Related Links
+
+- [Chrome Platform Status - Feature: Declarative PendingBeacon API][status]
+- [Pending Beacon Explainer on GitHub](https://github.com/WICG/pending-beacon#readme)
+- [Pending Beacon API Spec (draft)](https://wicg.github.io/pending-beacon/)
+- Ask questions about API & Spec via [new issue](https://github.com/WICG/pending-beacon/issues/new)
+- [Pending Beacon Design Doc in Chromium](https://docs.google.com/document/d/1QIFUu6Ne8x0W62RKJSoTtZjSd_bIM2yXZSELxdeuTFo/edit#)
+
+[status]: https://chromestatus.com/feature/5690553554436096
diff --git a/extensions/renderer/bindings/api_response_validator.cc b/extensions/renderer/bindings/api_response_validator.cc
index 8f7f571a..d8d06b7d 100644
--- a/extensions/renderer/bindings/api_response_validator.cc
+++ b/extensions/renderer/bindings/api_response_validator.cc
@@ -122,9 +122,14 @@
       "input.ime.onFocus",
       "inputMethodPrivate.onFocus",
       "test.onMessage",
+
       // https://crbug.com/1343611.
       "runtime.onMessage",
+      "runtime.onConnect",
       "contextMenus.onClicked",
+
+      // https://crbug.com/1375903.
+      "downloads.onCreated",
   };
 
   if (base::ranges::find(kBrokenSignaturesToIgnore, event_name) !=
diff --git a/extensions/renderer/resources/set_icon.js b/extensions/renderer/resources/set_icon.js
index 0418e0f12e..b1c9b89 100644
--- a/extensions/renderer/resources/set_icon.js
+++ b/extensions/renderer/resources/set_icon.js
@@ -137,11 +137,21 @@
       var detailKeyCount = 0;
       for (var iconSize in details.path) {
         ++detailKeyCount;
-        loadImagePath(details.path[iconSize], function(size, imageData) {
-          details.imageData[size] = imageData;
-          if (--detailKeyCount == 0)
-            callback(SetIconCommon(details));
-        }.bind(null, iconSize), failureCallback);
+        loadImagePath(
+            details.path[iconSize],
+            function(size, imageData) {
+              details.imageData[size] = imageData;
+              if (--detailKeyCount == 0) {
+                callback(SetIconCommon(details));
+              }
+            }.bind(null, iconSize),
+            function(errorMessage) {
+              if (failureCallback) {
+                failureCallback(errorMessage);
+                // Only report the first error.
+                failureCallback = null;
+              }
+            });
       }
       if (detailKeyCount == 0)
         throw new Error('The path property must not be empty.');
diff --git a/extensions/shell/browser/shell_audio_controller_chromeos_unittest.cc b/extensions/shell/browser/shell_audio_controller_chromeos_unittest.cc
index 04da0d6..682a42d 100644
--- a/extensions/shell/browser/shell_audio_controller_chromeos_unittest.cc
+++ b/extensions/shell/browser/shell_audio_controller_chromeos_unittest.cc
@@ -126,8 +126,7 @@
 
   EXPECT_FALSE(audio_handler()->IsOutputMuted());
   EXPECT_FALSE(audio_handler()->IsInputMuted());
-  EXPECT_EQ(static_cast<double>(
-                ash::AudioDevicesPrefHandler::kDefaultOutputVolumePercent),
+  EXPECT_EQ(ash::AudioDevicesPrefHandler::kDefaultOutputVolumePercent,
             audio_handler()->GetOutputVolumePercent());
 
   EXPECT_EQ(75.0, audio_handler()->GetInputGainPercent());
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg
index 35b417f..d72bbe9 100644
--- a/infra/config/generated/luci/realms.cfg
+++ b/infra/config/generated/luci/realms.cfg
@@ -25,6 +25,7 @@
   }
   bindings {
     role: "role/configs.validator"
+    principals: "group:project-chromium-ci-task-accounts"
     principals: "group:project-chromium-try-task-accounts"
   }
   bindings {
diff --git a/infra/config/main.star b/infra/config/main.star
index c6b0468..8ea02b7 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -102,7 +102,10 @@
     bindings = [
         luci.binding(
             roles = "role/configs.validator",
-            groups = "project-chromium-try-task-accounts",
+            groups = [
+                "project-chromium-try-task-accounts",
+                "project-chromium-ci-task-accounts",
+            ],
         ),
         # Roles for LUCI Analysis.
         luci.binding(
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index cb69b63..316c791 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -55,7 +55,7 @@
 chrome/browser/ui 2 1
 chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox 1 1
 chrome/browser/ui/commander 1 1
-chrome/browser/ui/webui/chromeos/parent_access 1 1
+chrome/browser/ui/webui/ash/parent_access 1 1
 chrome/browser/ui/webui/settings 1 1
 chrome/browser/win 2 1
 chromecast 1 1
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 42285e3..97e4df66 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -68,13 +68,6 @@
 
   if (is_chromeos) {
     sources += [
-      "v4l2_video_decoder_delegate_h264_legacy.cc",
-      "v4l2_video_decoder_delegate_h264_legacy.h",
-      "v4l2_video_decoder_delegate_vp8_legacy.cc",
-      "v4l2_video_decoder_delegate_vp8_legacy.h",
-      "v4l2_video_decoder_delegate_vp9_legacy.cc",
-      "v4l2_video_decoder_delegate_vp9_legacy.h",
-
       # TODO(crbug.com/901264): Encoders use hack for passing offset
       # within a DMA-buf, which is not supported upstream.
       "v4l2_video_encode_accelerator.cc",
diff --git a/media/gpu/v4l2/v4l2_decode_surface.cc b/media/gpu/v4l2/v4l2_decode_surface.cc
index 8fe97c36..bf3edd1 100644
--- a/media/gpu/v4l2/v4l2_decode_surface.cc
+++ b/media/gpu/v4l2/v4l2_decode_surface.cc
@@ -103,47 +103,6 @@
   return out;
 }
 
-// ConfigStore is ChromeOS-specific legacy stuff
-#if BUILDFLAG(IS_CHROMEOS)
-void V4L2ConfigStoreDecodeSurface::PrepareSetCtrls(
-    struct v4l2_ext_controls* ctrls) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_NE(ctrls, nullptr);
-  DCHECK_GT(config_store_, 0u);
-
-  ctrls->config_store = config_store_;
-}
-
-uint64_t V4L2ConfigStoreDecodeSurface::GetReferenceID() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Control store uses the output buffer ID as reference.
-  return output_record();
-}
-
-bool V4L2ConfigStoreDecodeSurface::Submit() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_GT(config_store_, 0u);
-
-  input_buffer().SetConfigStore(config_store_);
-
-  if (!std::move(input_buffer()).QueueMMap()) {
-    return false;
-  }
-
-  switch (output_buffer().Memory()) {
-    case V4L2_MEMORY_MMAP:
-      return std::move(output_buffer()).QueueMMap();
-    case V4L2_MEMORY_DMABUF:
-      return std::move(output_buffer()).QueueDMABuf(video_frame());
-    default:
-      NOTREACHED() << "We should only use MMAP or DMABUF.";
-  }
-
-  return false;
-}
-#endif
-
 void V4L2RequestDecodeSurface::PrepareSetCtrls(
     struct v4l2_ext_controls* ctrls) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/media/gpu/v4l2/v4l2_decode_surface.h b/media/gpu/v4l2/v4l2_decode_surface.h
index e789e4f..90c11a67 100644
--- a/media/gpu/v4l2/v4l2_decode_surface.h
+++ b/media/gpu/v4l2/v4l2_decode_surface.h
@@ -111,38 +111,6 @@
   std::vector<scoped_refptr<V4L2DecodeSurface>> reference_surfaces_;
 };
 
-// ConfigStore is ChromeOS-specific legacy stuff
-// TODO(b/222774780): Remove when all legacy implementations are gone.
-#if BUILDFLAG(IS_CHROMEOS)
-// An implementation of V4L2DecodeSurface that uses the config store to
-// associate controls/buffers to frames.
-class V4L2ConfigStoreDecodeSurface : public V4L2DecodeSurface {
- public:
-  V4L2ConfigStoreDecodeSurface(V4L2WritableBufferRef input_buffer,
-                               V4L2WritableBufferRef output_buffer,
-                               scoped_refptr<VideoFrame> frame)
-      : V4L2DecodeSurface(std::move(input_buffer),
-                          std::move(output_buffer),
-                          std::move(frame)),
-        // config store IDs are arbitrarily defined to be buffer ID + 1
-        config_store_(this->input_buffer().BufferId() + 1) {}
-
-  V4L2ConfigStoreDecodeSurface(const V4L2ConfigStoreDecodeSurface&) = delete;
-  V4L2ConfigStoreDecodeSurface& operator=(const V4L2ConfigStoreDecodeSurface&) =
-      delete;
-
-  void PrepareSetCtrls(struct v4l2_ext_controls* ctrls) const override;
-  uint64_t GetReferenceID() const override;
-  bool Submit() override;
-
- private:
-  ~V4L2ConfigStoreDecodeSurface() override = default;
-
-  // The configuration store of the input buffer.
-  uint32_t config_store_;
-};
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 // An implementation of V4L2DecodeSurface that uses requests to associate
 // controls/buffers to frames
 class V4L2RequestDecodeSurface : public V4L2DecodeSurface {
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index 79d6be6..a86bcad 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -44,11 +44,8 @@
 #include "media/gpu/v4l2/v4l2_utils.h"
 #include "media/gpu/v4l2/v4l2_vda_helpers.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_h264.h"
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp8.h"
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.h"
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.h"
 #include "ui/gfx/native_pixmap_handle.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
@@ -275,14 +272,9 @@
   reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
   reqbufs.memory = V4L2_MEMORY_MMAP;
   IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
-  if (reqbufs.capabilities & V4L2_BUF_CAP_SUPPORTS_REQUESTS) {
-    // Implicitly this is a way to differentiate between old kernels (pre 5.x)
-    // where the request API was not present.
-    supports_requests_ = true;
-    VLOGF(1) << "Using request API";
-  } else {
-    VLOGF(1) << "Using config store";
-  }
+  // Implicitly this is a way to differentiate between old kernels (pre 5.x)
+  // where the request API was not present.
+  CHECK(reqbufs.capabilities & V4L2_BUF_CAP_SUPPORTS_REQUESTS);
 
   // Check if |video_profile_| is supported by a decoder driver.
   if (!IsSupportedProfile(video_profile_)) {
@@ -291,64 +283,27 @@
   }
 
   if (video_profile_ >= H264PROFILE_MIN && video_profile_ <= H264PROFILE_MAX) {
-    if (supports_requests_) {
-      decoder_ = std::make_unique<H264Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateH264>(this, device_.get()),
-          video_profile_, config.container_color_space);
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      decoder_ = std::make_unique<H264Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateH264Legacy>(this,
-                                                               device_.get()),
-          video_profile_, config.container_color_space);
-#else
-      NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
-      return false;
-#endif
-    }
+    decoder_ = std::make_unique<H264Decoder>(
+        std::make_unique<V4L2VideoDecoderDelegateH264>(this, device_.get()),
+        video_profile_, config.container_color_space);
   } else if (video_profile_ >= VP8PROFILE_MIN &&
              video_profile_ <= VP8PROFILE_MAX) {
-    if (supports_requests_) {
-      decoder_ = std::make_unique<VP8Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP8>(this, device_.get()),
-          config.container_color_space);
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      decoder_ = std::make_unique<VP8Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP8Legacy>(this,
-                                                              device_.get()),
-          config.container_color_space);
-#else
-      NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
-      return false;
-#endif
-    }
+    decoder_ = std::make_unique<VP8Decoder>(
+        std::make_unique<V4L2VideoDecoderDelegateVP8>(this, device_.get()),
+        config.container_color_space);
   } else if (video_profile_ >= VP9PROFILE_MIN &&
              video_profile_ <= VP9PROFILE_MAX) {
-    if (supports_requests_) {
-      // TODO(mcasas): Remove this ifndef when V4L2_CID_STATELESS_VP9_FRAME is
-      // known in all kernels.
+    // TODO(mcasas): Remove this ifndef when V4L2_CID_STATELESS_VP9_FRAME is
+    // known in all kernels.
 #ifndef V4L2_CID_STATELESS_VP9_FRAME
 #define V4L2_CID_STATELESS_VP9_FRAME (0x00a40900 + 300)
 #endif
-      const bool supports_stable_api =
-          device_->IsCtrlExposed(V4L2_CID_STATELESS_VP9_FRAME);
-      CHECK(supports_stable_api);
-      decoder_ = std::make_unique<VP9Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP9>(this, device_.get()),
-          video_profile_, config.container_color_space);
-
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      decoder_ = std::make_unique<VP9Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP9Legacy>(this,
-                                                              device_.get()),
-          video_profile_, config.container_color_space);
-#else
-      NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
-      return false;
-#endif
-    }
+    const bool supports_stable_api =
+        device_->IsCtrlExposed(V4L2_CID_STATELESS_VP9_FRAME);
+    CHECK(supports_stable_api);
+    decoder_ = std::make_unique<VP9Decoder>(
+        std::make_unique<V4L2VideoDecoderDelegateVP9>(this, device_.get()),
+        video_profile_, config.container_color_space);
   } else {
     NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
     return false;
@@ -664,13 +619,9 @@
     return false;
   }
 
-  if (supports_requests_) {
-    requests_queue_ = device_->GetRequestsQueue();
-    if (requests_queue_ == nullptr)
-      return false;
-  }
-
-  return true;
+  CHECK(input_queue_->SupportsRequests());
+  requests_queue_ = device_->GetRequestsQueue();
+  return !requests_queue_;
 }
 
 bool V4L2SliceVideoDecodeAccelerator::CreateOutputBuffers() {
@@ -2129,41 +2080,26 @@
   if (!input_buffer || !output_buffer)
     return nullptr;
 
-  int input = input_buffer->BufferId();
-  int output = output_buffer->BufferId();
+  const int input = input_buffer->BufferId();
+  const int output = output_buffer->BufferId();
 
-  scoped_refptr<V4L2DecodeSurface> dec_surface;
-
-  size_t index = output_buffer->BufferId();
+  const size_t index = output_buffer->BufferId();
   OutputRecord& output_record = output_buffer_map_[index];
   DCHECK_NE(output_record.picture_id, -1);
 
-  if (supports_requests_) {
-    // Get a free request from the queue for a new surface.
-    absl::optional<V4L2RequestRef> request_ref =
-        requests_queue_->GetFreeRequest();
-    if (!request_ref) {
-      LOG(ERROR) << "Failed getting a request";
-      NOTIFY_ERROR(PLATFORM_FAILURE);
-      return nullptr;
-    }
-    dec_surface = new V4L2RequestDecodeSurface(
-        std::move(*input_buffer), std::move(*output_buffer),
-        output_record.output_frame, std::move(*request_ref));
-  } else {
-// ConfigStore is ChromeOS-specific legacy stuff
-#if BUILDFLAG(IS_CHROMEOS)
-    dec_surface = new V4L2ConfigStoreDecodeSurface(std::move(*input_buffer),
-                                                   std::move(*output_buffer),
-                                                   output_record.output_frame);
-#else
-    NOTREACHED() << "ConfigStore not supported.";
+  // Get a free request from the queue for a new surface.
+  absl::optional<V4L2RequestRef> request_ref =
+      requests_queue_->GetFreeRequest();
+  if (!request_ref) {
+    LOG(ERROR) << "Failed getting a request";
+    NOTIFY_ERROR(PLATFORM_FAILURE);
     return nullptr;
-#endif
   }
 
-  DVLOGF(4) << "Created surface " << input << " -> " << output;
-  return dec_surface;
+  DVLOGF(4) << __func__ << " " << input << " -> " << output;
+  return new V4L2RequestDecodeSurface(
+      std::move(*input_buffer), std::move(*output_buffer),
+      output_record.output_frame, std::move(*request_ref));
 }
 
 void V4L2SliceVideoDecodeAccelerator::SendPictureReady() {
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
index 142f92b..c1ae7d7 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
@@ -402,9 +402,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> decoder_thread_task_runner_;
 
   scoped_refptr<V4L2Queue> input_queue_;
-  // Set to true by CreateInputBuffers() if the codec driver supports requests
-  bool supports_requests_ = false;
-
   scoped_refptr<V4L2Queue> output_queue_;
   // Buffers that have been allocated but are awaiting an ImportBuffer
   // or AssignDmabufs event.
@@ -416,7 +413,7 @@
   // an image processor in ALLOCATE mode in which case the index of the IP
   // buffer may not match the one of the decoder.
   std::map<int32_t, int32_t> decoded_buffer_map_;
-  // FIFO queue of requests, only used if supports_requests_ == true.
+  // FIFO queue of requests.
   std::queue<base::ScopedFD> requests_;
 
   VideoCodecProfile video_profile_;
diff --git a/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc b/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
index 1905061..9cf9d6c 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
@@ -26,11 +26,8 @@
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_h264.h"
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp8.h"
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.h"
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.h"
 
 namespace media {
 
@@ -140,13 +137,9 @@
   if (!CreateDecoder())
     return false;
 
-  if (input_queue_->SupportsRequests()) {
-    requests_queue_ = device_->GetRequestsQueue();
-    if (requests_queue_ == nullptr)
-      return false;
-  }
-
-  return true;
+  CHECK(input_queue_->SupportsRequests());
+  requests_queue_ = device_->GetRequestsQueue();
+  return !requests_queue_;
 }
 
 // static
@@ -270,30 +263,17 @@
   }
 
   scoped_refptr<V4L2DecodeSurface> dec_surface;
-  if (input_queue_->SupportsRequests()) {
-    absl::optional<V4L2RequestRef> request_ref =
-        requests_queue_->GetFreeRequest();
-    if (!request_ref) {
-      DVLOGF(1) << "Could not get free request.";
-      return nullptr;
-    }
-
-    dec_surface = new V4L2RequestDecodeSurface(
-        std::move(*input_buf), std::move(*output_buf), std::move(frame),
-        std::move(*request_ref));
-  } else {
-    // ConfigStore is ChromeOS-specific legacy stuff
-    // TODO(b/222774780): Remove when all legacy implementations are gone.
-#if BUILDFLAG(IS_CHROMEOS)
-    dec_surface = new V4L2ConfigStoreDecodeSurface(
-        std::move(*input_buf), std::move(*output_buf), std::move(frame));
-#else
-    NOTREACHED() << "ConfigStore not supported.";
+  CHECK(input_queue_->SupportsRequests());
+  absl::optional<V4L2RequestRef> request_ref =
+      requests_queue_->GetFreeRequest();
+  if (!request_ref) {
+    DVLOGF(1) << "Could not get free request.";
     return nullptr;
-#endif
   }
 
-  return dec_surface;
+  return new V4L2RequestDecodeSurface(std::move(*input_buf),
+                                      std::move(*output_buf), std::move(frame),
+                                      std::move(*request_ref));
 }
 
 bool V4L2StatelessVideoDecoderBackend::SubmitSlice(
@@ -692,62 +672,28 @@
 
   pic_size_ = gfx::Size();
 
+  CHECK(input_queue_->SupportsRequests());
+
   if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) {
-    if (input_queue_->SupportsRequests()) {
-      decoder_ = std::make_unique<H264Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateH264>(this, device_.get()),
-          profile_, color_space_);
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      decoder_ = std::make_unique<H264Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateH264Legacy>(this,
-                                                               device_.get()),
-          profile_, color_space_);
-#else
-      VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
-      return false;
-#endif
-    }
+    decoder_ = std::make_unique<H264Decoder>(
+        std::make_unique<V4L2VideoDecoderDelegateH264>(this, device_.get()),
+        profile_, color_space_);
   } else if (profile_ >= VP8PROFILE_MIN && profile_ <= VP8PROFILE_MAX) {
-    if (input_queue_->SupportsRequests()) {
-      decoder_ = std::make_unique<VP8Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP8>(this, device_.get()),
-          color_space_);
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      decoder_ = std::make_unique<VP8Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP8Legacy>(this,
-                                                              device_.get()),
-          color_space_);
-#else
-      VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
-      return false;
-#endif
-    }
+    decoder_ = std::make_unique<VP8Decoder>(
+        std::make_unique<V4L2VideoDecoderDelegateVP8>(this, device_.get()),
+        color_space_);
   } else if (profile_ >= VP9PROFILE_MIN && profile_ <= VP9PROFILE_MAX) {
-    if (input_queue_->SupportsRequests()) {
-      // TODO(mcasas): Remove this ifndef when V4L2_CID_STATELESS_VP9_FRAME is
-      // known in all kernels.
+    // TODO(mcasas): Remove this ifndef when V4L2_CID_STATELESS_VP9_FRAME is
+    // known in all kernels.
 #ifndef V4L2_CID_STATELESS_VP9_FRAME
 #define V4L2_CID_STATELESS_VP9_FRAME (0x00a40900 + 300)
 #endif
-      const bool supports_stable_api =
-          device_->IsCtrlExposed(V4L2_CID_STATELESS_VP9_FRAME);
-      CHECK(supports_stable_api);
-      decoder_ = std::make_unique<VP9Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP9>(this, device_.get()),
-          profile_, color_space_);
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      decoder_ = std::make_unique<VP9Decoder>(
-          std::make_unique<V4L2VideoDecoderDelegateVP9Legacy>(this,
-                                                              device_.get()),
-          profile_, color_space_);
-#else
-      VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
-      return false;
-#endif
-    }
+    const bool supports_stable_api =
+        device_->IsCtrlExposed(V4L2_CID_STATELESS_VP9_FRAME);
+    CHECK(supports_stable_api);
+    decoder_ = std::make_unique<VP9Decoder>(
+        std::make_unique<V4L2VideoDecoderDelegateVP9>(this, device_.get()),
+        profile_, color_space_);
   } else {
     VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
     return false;
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.cc
deleted file mode 100644
index bf8823d8..0000000
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.cc
+++ /dev/null
@@ -1,492 +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 "media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h"
-
-#include <linux/media/h264-ctrls-legacy.h>
-#include <linux/videodev2.h>
-
-#include <type_traits>
-
-#include "base/logging.h"
-#include "media/gpu/macros.h"
-#include "media/gpu/v4l2/v4l2_decode_surface.h"
-#include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
-#include "media/gpu/v4l2/v4l2_device.h"
-
-namespace media {
-
-// This struct contains the kernel-specific parts of the H264 acceleration,
-// that we don't want to expose in the .h file since they may differ from
-// upstream.
-struct V4L2VideoDecoderDelegateH264LegacyPrivate {
-  // TODO(posciak): This should be queried from hardware once supported.
-  static constexpr size_t kMaxSlices = 16;
-
-  struct v4l2_ctrl_h264_slice_param v4l2_slice_params[kMaxSlices];
-  struct v4l2_ctrl_h264_decode_param v4l2_decode_param;
-};
-
-class V4L2H264Picture : public H264Picture {
- public:
-  explicit V4L2H264Picture(scoped_refptr<V4L2DecodeSurface> dec_surface)
-      : dec_surface_(std::move(dec_surface)) {}
-
-  V4L2H264Picture(const V4L2H264Picture&) = delete;
-  V4L2H264Picture& operator=(const V4L2H264Picture&) = delete;
-
-  V4L2H264Picture* AsV4L2H264Picture() override { return this; }
-  scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
-
- private:
-  ~V4L2H264Picture() override {}
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface_;
-};
-
-V4L2VideoDecoderDelegateH264Legacy::V4L2VideoDecoderDelegateH264Legacy(
-    V4L2DecodeSurfaceHandler* surface_handler,
-    V4L2Device* device)
-    : num_slices_(0),
-      surface_handler_(surface_handler),
-      device_(device),
-      priv_(std::make_unique<V4L2VideoDecoderDelegateH264LegacyPrivate>()) {
-  DCHECK(surface_handler_);
-}
-
-V4L2VideoDecoderDelegateH264Legacy::~V4L2VideoDecoderDelegateH264Legacy() {}
-
-scoped_refptr<H264Picture>
-V4L2VideoDecoderDelegateH264Legacy::CreateH264Picture() {
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      surface_handler_->CreateSurface();
-  if (!dec_surface)
-    return nullptr;
-
-  return new V4L2H264Picture(dec_surface);
-}
-
-void V4L2VideoDecoderDelegateH264Legacy::H264PictureListToDPBIndicesList(
-    const H264Picture::Vector& src_pic_list,
-    uint8_t dst_list[kDPBIndicesListSize]) {
-  size_t i;
-  for (i = 0; i < src_pic_list.size() && i < kDPBIndicesListSize; ++i) {
-    const H264Picture* pic = src_pic_list[i].get();
-    dst_list[i] = pic ? pic->dpb_position : VIDEO_MAX_FRAME;
-  }
-
-  while (i < kDPBIndicesListSize)
-    dst_list[i++] = VIDEO_MAX_FRAME;
-}
-
-void V4L2VideoDecoderDelegateH264Legacy::H264DPBToV4L2DPB(
-    const H264DPB& dpb,
-    std::vector<scoped_refptr<V4L2DecodeSurface>>* ref_surfaces) {
-  memset(priv_->v4l2_decode_param.dpb, 0, sizeof(priv_->v4l2_decode_param.dpb));
-  size_t i = 0;
-  for (const auto& pic : dpb) {
-    if (i >= std::size(priv_->v4l2_decode_param.dpb)) {
-      VLOGF(1) << "Invalid DPB size";
-      break;
-    }
-
-    int index = VIDEO_MAX_FRAME;
-    if (!pic->nonexisting) {
-      scoped_refptr<V4L2DecodeSurface> dec_surface =
-          H264PictureToV4L2DecodeSurface(pic.get());
-      index = dec_surface->GetReferenceID();
-      ref_surfaces->push_back(dec_surface);
-    }
-
-    struct v4l2_h264_dpb_entry& entry = priv_->v4l2_decode_param.dpb[i++];
-    entry.buf_index = index;
-    if (pic->long_term) {
-      entry.frame_num = pic->long_term_pic_num;
-      entry.pic_num = pic->long_term_frame_idx;
-    } else {
-      entry.frame_num = pic->frame_num;
-      entry.pic_num = pic->pic_num;
-    }
-    entry.top_field_order_cnt = pic->top_field_order_cnt;
-    entry.bottom_field_order_cnt = pic->bottom_field_order_cnt;
-    entry.flags = (pic->ref ? V4L2_H264_DPB_ENTRY_FLAG_ACTIVE : 0) |
-                  (pic->long_term ? V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM : 0);
-  }
-}
-
-H264Decoder::H264Accelerator::Status
-V4L2VideoDecoderDelegateH264Legacy::SubmitFrameMetadata(
-    const H264SPS* sps,
-    const H264PPS* pps,
-    const H264DPB& dpb,
-    const H264Picture::Vector& ref_pic_listp0,
-    const H264Picture::Vector& ref_pic_listb0,
-    const H264Picture::Vector& ref_pic_listb1,
-    scoped_refptr<H264Picture> pic) {
-  struct v4l2_ext_control ctrl;
-  std::vector<struct v4l2_ext_control> ctrls;
-
-  struct v4l2_ctrl_h264_sps v4l2_sps;
-  memset(&v4l2_sps, 0, sizeof(v4l2_sps));
-  v4l2_sps.constraint_set_flags =
-      (sps->constraint_set0_flag ? V4L2_H264_SPS_CONSTRAINT_SET0_FLAG : 0) |
-      (sps->constraint_set1_flag ? V4L2_H264_SPS_CONSTRAINT_SET1_FLAG : 0) |
-      (sps->constraint_set2_flag ? V4L2_H264_SPS_CONSTRAINT_SET2_FLAG : 0) |
-      (sps->constraint_set3_flag ? V4L2_H264_SPS_CONSTRAINT_SET3_FLAG : 0) |
-      (sps->constraint_set4_flag ? V4L2_H264_SPS_CONSTRAINT_SET4_FLAG : 0) |
-      (sps->constraint_set5_flag ? V4L2_H264_SPS_CONSTRAINT_SET5_FLAG : 0);
-#define SPS_TO_V4L2SPS(a) v4l2_sps.a = sps->a
-  SPS_TO_V4L2SPS(profile_idc);
-  SPS_TO_V4L2SPS(level_idc);
-  SPS_TO_V4L2SPS(seq_parameter_set_id);
-  SPS_TO_V4L2SPS(chroma_format_idc);
-  SPS_TO_V4L2SPS(bit_depth_luma_minus8);
-  SPS_TO_V4L2SPS(bit_depth_chroma_minus8);
-  SPS_TO_V4L2SPS(log2_max_frame_num_minus4);
-  SPS_TO_V4L2SPS(pic_order_cnt_type);
-  SPS_TO_V4L2SPS(log2_max_pic_order_cnt_lsb_minus4);
-  SPS_TO_V4L2SPS(offset_for_non_ref_pic);
-  SPS_TO_V4L2SPS(offset_for_top_to_bottom_field);
-  SPS_TO_V4L2SPS(num_ref_frames_in_pic_order_cnt_cycle);
-
-  static_assert(std::extent<decltype(v4l2_sps.offset_for_ref_frame)>() ==
-                    std::extent<decltype(sps->offset_for_ref_frame)>(),
-                "offset_for_ref_frame arrays must be same size");
-  for (size_t i = 0; i < std::size(v4l2_sps.offset_for_ref_frame); ++i)
-    v4l2_sps.offset_for_ref_frame[i] = sps->offset_for_ref_frame[i];
-  SPS_TO_V4L2SPS(max_num_ref_frames);
-  SPS_TO_V4L2SPS(pic_width_in_mbs_minus1);
-  SPS_TO_V4L2SPS(pic_height_in_map_units_minus1);
-#undef SPS_TO_V4L2SPS
-
-#define SET_V4L2_SPS_FLAG_IF(cond, flag) \
-  v4l2_sps.flags |= ((sps->cond) ? (flag) : 0)
-  SET_V4L2_SPS_FLAG_IF(separate_colour_plane_flag,
-                       V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE);
-  SET_V4L2_SPS_FLAG_IF(qpprime_y_zero_transform_bypass_flag,
-                       V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
-  SET_V4L2_SPS_FLAG_IF(delta_pic_order_always_zero_flag,
-                       V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
-  SET_V4L2_SPS_FLAG_IF(gaps_in_frame_num_value_allowed_flag,
-                       V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED);
-  SET_V4L2_SPS_FLAG_IF(frame_mbs_only_flag, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
-  SET_V4L2_SPS_FLAG_IF(mb_adaptive_frame_field_flag,
-                       V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
-  SET_V4L2_SPS_FLAG_IF(direct_8x8_inference_flag,
-                       V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
-#undef SET_V4L2_SPS_FLAG_IF
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_H264_SPS;
-  ctrl.size = sizeof(v4l2_sps);
-  ctrl.ptr = &v4l2_sps;
-  ctrls.push_back(ctrl);
-
-  struct v4l2_ctrl_h264_pps v4l2_pps;
-  memset(&v4l2_pps, 0, sizeof(v4l2_pps));
-#define PPS_TO_V4L2PPS(a) v4l2_pps.a = pps->a
-  PPS_TO_V4L2PPS(pic_parameter_set_id);
-  PPS_TO_V4L2PPS(seq_parameter_set_id);
-  PPS_TO_V4L2PPS(num_slice_groups_minus1);
-  PPS_TO_V4L2PPS(num_ref_idx_l0_default_active_minus1);
-  PPS_TO_V4L2PPS(num_ref_idx_l1_default_active_minus1);
-  PPS_TO_V4L2PPS(weighted_bipred_idc);
-  PPS_TO_V4L2PPS(pic_init_qp_minus26);
-  PPS_TO_V4L2PPS(pic_init_qs_minus26);
-  PPS_TO_V4L2PPS(chroma_qp_index_offset);
-  PPS_TO_V4L2PPS(second_chroma_qp_index_offset);
-#undef PPS_TO_V4L2PPS
-
-#define SET_V4L2_PPS_FLAG_IF(cond, flag) \
-  v4l2_pps.flags |= ((pps->cond) ? (flag) : 0)
-  SET_V4L2_PPS_FLAG_IF(entropy_coding_mode_flag,
-                       V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
-  SET_V4L2_PPS_FLAG_IF(
-      bottom_field_pic_order_in_frame_present_flag,
-      V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
-  SET_V4L2_PPS_FLAG_IF(weighted_pred_flag, V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
-  SET_V4L2_PPS_FLAG_IF(deblocking_filter_control_present_flag,
-                       V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
-  SET_V4L2_PPS_FLAG_IF(constrained_intra_pred_flag,
-                       V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
-  SET_V4L2_PPS_FLAG_IF(redundant_pic_cnt_present_flag,
-                       V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
-  SET_V4L2_PPS_FLAG_IF(transform_8x8_mode_flag,
-                       V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
-  SET_V4L2_PPS_FLAG_IF(pic_scaling_matrix_present_flag,
-                       V4L2_H264_PPS_FLAG_PIC_SCALING_MATRIX_PRESENT);
-#undef SET_V4L2_PPS_FLAG_IF
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_H264_PPS;
-  ctrl.size = sizeof(v4l2_pps);
-  ctrl.ptr = &v4l2_pps;
-  ctrls.push_back(ctrl);
-
-  struct v4l2_ctrl_h264_scaling_matrix v4l2_scaling_matrix;
-  memset(&v4l2_scaling_matrix, 0, sizeof(v4l2_scaling_matrix));
-
-  static_assert(
-      std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4)>() <=
-              std::extent<decltype(pps->scaling_list4x4)>() &&
-          std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4[0])>() <=
-              std::extent<decltype(pps->scaling_list4x4[0])>() &&
-          std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8)>() <=
-              std::extent<decltype(pps->scaling_list8x8)>() &&
-          std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8[0])>() <=
-              std::extent<decltype(pps->scaling_list8x8[0])>(),
-      "scaling_lists must be of correct size");
-  static_assert(
-      std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4)>() <=
-              std::extent<decltype(sps->scaling_list4x4)>() &&
-          std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4[0])>() <=
-              std::extent<decltype(sps->scaling_list4x4[0])>() &&
-          std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8)>() <=
-              std::extent<decltype(sps->scaling_list8x8)>() &&
-          std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8[0])>() <=
-              std::extent<decltype(sps->scaling_list8x8[0])>(),
-      "scaling_lists must be of correct size");
-
-  const auto* scaling_list4x4 = &sps->scaling_list4x4[0];
-  const auto* scaling_list8x8 = &sps->scaling_list8x8[0];
-  if (pps->pic_scaling_matrix_present_flag) {
-    scaling_list4x4 = &pps->scaling_list4x4[0];
-    scaling_list8x8 = &pps->scaling_list8x8[0];
-  }
-
-  for (size_t i = 0; i < std::size(v4l2_scaling_matrix.scaling_list_4x4); ++i) {
-    for (size_t j = 0; j < std::size(v4l2_scaling_matrix.scaling_list_4x4[i]);
-         ++j) {
-      v4l2_scaling_matrix.scaling_list_4x4[i][j] = scaling_list4x4[i][j];
-    }
-  }
-  for (size_t i = 0; i < std::size(v4l2_scaling_matrix.scaling_list_8x8); ++i) {
-    for (size_t j = 0; j < std::size(v4l2_scaling_matrix.scaling_list_8x8[i]);
-         ++j) {
-      v4l2_scaling_matrix.scaling_list_8x8[i][j] = scaling_list8x8[i][j];
-    }
-  }
-
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX;
-  ctrl.size = sizeof(v4l2_scaling_matrix);
-  ctrl.ptr = &v4l2_scaling_matrix;
-  ctrls.push_back(ctrl);
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      H264PictureToV4L2DecodeSurface(pic.get());
-
-  struct v4l2_ext_controls ext_ctrls;
-  memset(&ext_ctrls, 0, sizeof(ext_ctrls));
-  ext_ctrls.count = ctrls.size();
-  ext_ctrls.controls = &ctrls[0];
-  dec_surface->PrepareSetCtrls(&ext_ctrls);
-  if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
-    VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
-    return Status::kFail;
-  }
-
-  H264PictureListToDPBIndicesList(ref_pic_listp0,
-                                  priv_->v4l2_decode_param.ref_pic_list_p0);
-  H264PictureListToDPBIndicesList(ref_pic_listb0,
-                                  priv_->v4l2_decode_param.ref_pic_list_b0);
-  H264PictureListToDPBIndicesList(ref_pic_listb1,
-                                  priv_->v4l2_decode_param.ref_pic_list_b1);
-
-  std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces;
-  H264DPBToV4L2DPB(dpb, &ref_surfaces);
-  dec_surface->SetReferenceSurfaces(ref_surfaces);
-
-  return Status::kOk;
-}
-
-H264Decoder::H264Accelerator::Status
-V4L2VideoDecoderDelegateH264Legacy::SubmitSlice(
-    const H264PPS* pps,
-    const H264SliceHeader* slice_hdr,
-    const H264Picture::Vector& ref_pic_list0,
-    const H264Picture::Vector& ref_pic_list1,
-    scoped_refptr<H264Picture> pic,
-    const uint8_t* data,
-    size_t size,
-    const std::vector<SubsampleEntry>& subsamples) {
-  if (num_slices_ == priv_->kMaxSlices) {
-    VLOGF(1) << "Over limit of supported slices per frame";
-    return Status::kFail;
-  }
-
-  struct v4l2_ctrl_h264_slice_param& v4l2_slice_param =
-      priv_->v4l2_slice_params[num_slices_++];
-  memset(&v4l2_slice_param, 0, sizeof(v4l2_slice_param));
-
-  v4l2_slice_param.size = size;
-#define SHDR_TO_V4L2SPARM(a) v4l2_slice_param.a = slice_hdr->a
-  SHDR_TO_V4L2SPARM(header_bit_size);
-  SHDR_TO_V4L2SPARM(first_mb_in_slice);
-  SHDR_TO_V4L2SPARM(slice_type);
-  SHDR_TO_V4L2SPARM(pic_parameter_set_id);
-  SHDR_TO_V4L2SPARM(colour_plane_id);
-  SHDR_TO_V4L2SPARM(frame_num);
-  SHDR_TO_V4L2SPARM(idr_pic_id);
-  SHDR_TO_V4L2SPARM(pic_order_cnt_lsb);
-  SHDR_TO_V4L2SPARM(delta_pic_order_cnt_bottom);
-  SHDR_TO_V4L2SPARM(delta_pic_order_cnt0);
-  SHDR_TO_V4L2SPARM(delta_pic_order_cnt1);
-  SHDR_TO_V4L2SPARM(redundant_pic_cnt);
-  SHDR_TO_V4L2SPARM(dec_ref_pic_marking_bit_size);
-  SHDR_TO_V4L2SPARM(cabac_init_idc);
-  SHDR_TO_V4L2SPARM(slice_qp_delta);
-  SHDR_TO_V4L2SPARM(slice_qs_delta);
-  SHDR_TO_V4L2SPARM(disable_deblocking_filter_idc);
-  SHDR_TO_V4L2SPARM(slice_alpha_c0_offset_div2);
-  SHDR_TO_V4L2SPARM(slice_beta_offset_div2);
-  SHDR_TO_V4L2SPARM(num_ref_idx_l0_active_minus1);
-  SHDR_TO_V4L2SPARM(num_ref_idx_l1_active_minus1);
-  SHDR_TO_V4L2SPARM(pic_order_cnt_bit_size);
-#undef SHDR_TO_V4L2SPARM
-
-#define SET_V4L2_SPARM_FLAG_IF(cond, flag) \
-  v4l2_slice_param.flags |= ((slice_hdr->cond) ? (flag) : 0)
-  SET_V4L2_SPARM_FLAG_IF(field_pic_flag, V4L2_SLICE_FLAG_FIELD_PIC);
-  SET_V4L2_SPARM_FLAG_IF(bottom_field_flag, V4L2_SLICE_FLAG_BOTTOM_FIELD);
-  SET_V4L2_SPARM_FLAG_IF(direct_spatial_mv_pred_flag,
-                         V4L2_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED);
-  SET_V4L2_SPARM_FLAG_IF(sp_for_switch_flag, V4L2_SLICE_FLAG_SP_FOR_SWITCH);
-#undef SET_V4L2_SPARM_FLAG_IF
-
-  struct v4l2_h264_pred_weight_table* pred_weight_table =
-      &v4l2_slice_param.pred_weight_table;
-
-  if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) &&
-       pps->weighted_pred_flag) ||
-      (slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) {
-    pred_weight_table->luma_log2_weight_denom =
-        slice_hdr->luma_log2_weight_denom;
-    pred_weight_table->chroma_log2_weight_denom =
-        slice_hdr->chroma_log2_weight_denom;
-
-    struct v4l2_h264_weight_factors* factorsl0 =
-        &pred_weight_table->weight_factors[0];
-
-    for (int i = 0; i < 32; ++i) {
-      factorsl0->luma_weight[i] =
-          slice_hdr->pred_weight_table_l0.luma_weight[i];
-      factorsl0->luma_offset[i] =
-          slice_hdr->pred_weight_table_l0.luma_offset[i];
-
-      for (int j = 0; j < 2; ++j) {
-        factorsl0->chroma_weight[i][j] =
-            slice_hdr->pred_weight_table_l0.chroma_weight[i][j];
-        factorsl0->chroma_offset[i][j] =
-            slice_hdr->pred_weight_table_l0.chroma_offset[i][j];
-      }
-    }
-
-    if (slice_hdr->IsBSlice()) {
-      struct v4l2_h264_weight_factors* factorsl1 =
-          &pred_weight_table->weight_factors[1];
-
-      for (int i = 0; i < 32; ++i) {
-        factorsl1->luma_weight[i] =
-            slice_hdr->pred_weight_table_l1.luma_weight[i];
-        factorsl1->luma_offset[i] =
-            slice_hdr->pred_weight_table_l1.luma_offset[i];
-
-        for (int j = 0; j < 2; ++j) {
-          factorsl1->chroma_weight[i][j] =
-              slice_hdr->pred_weight_table_l1.chroma_weight[i][j];
-          factorsl1->chroma_offset[i][j] =
-              slice_hdr->pred_weight_table_l1.chroma_offset[i][j];
-        }
-      }
-    }
-  }
-
-  H264PictureListToDPBIndicesList(ref_pic_list0,
-                                  v4l2_slice_param.ref_pic_list0);
-  H264PictureListToDPBIndicesList(ref_pic_list1,
-                                  v4l2_slice_param.ref_pic_list1);
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      H264PictureToV4L2DecodeSurface(pic.get());
-
-  priv_->v4l2_decode_param.nal_ref_idc = slice_hdr->nal_ref_idc;
-
-  // TODO(posciak): Don't add start code back here, but have it passed from
-  // the parser.
-  size_t data_copy_size = size + 3;
-  std::unique_ptr<uint8_t[]> data_copy(new uint8_t[data_copy_size]);
-  memset(data_copy.get(), 0, data_copy_size);
-  data_copy[2] = 0x01;
-  memcpy(data_copy.get() + 3, data, size);
-  return surface_handler_->SubmitSlice(dec_surface.get(), data_copy.get(),
-                                       data_copy_size)
-             ? Status::kOk
-             : Status::kFail;
-}
-
-H264Decoder::H264Accelerator::Status
-V4L2VideoDecoderDelegateH264Legacy::SubmitDecode(
-    scoped_refptr<H264Picture> pic) {
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      H264PictureToV4L2DecodeSurface(pic.get());
-
-  priv_->v4l2_decode_param.num_slices = num_slices_;
-  priv_->v4l2_decode_param.idr_pic_flag = pic->idr;
-  priv_->v4l2_decode_param.top_field_order_cnt = pic->top_field_order_cnt;
-  priv_->v4l2_decode_param.bottom_field_order_cnt = pic->bottom_field_order_cnt;
-
-  struct v4l2_ext_control ctrl;
-  std::vector<struct v4l2_ext_control> ctrls;
-
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM;
-  ctrl.size = sizeof(priv_->v4l2_slice_params);
-  ctrl.ptr = priv_->v4l2_slice_params;
-  ctrls.push_back(ctrl);
-
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM;
-  ctrl.size = sizeof(priv_->v4l2_decode_param);
-  ctrl.ptr = &priv_->v4l2_decode_param;
-  ctrls.push_back(ctrl);
-
-  struct v4l2_ext_controls ext_ctrls;
-  memset(&ext_ctrls, 0, sizeof(ext_ctrls));
-  ext_ctrls.count = ctrls.size();
-  ext_ctrls.controls = &ctrls[0];
-  dec_surface->PrepareSetCtrls(&ext_ctrls);
-  if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
-    VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
-    return Status::kFail;
-  }
-
-  Reset();
-
-  DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
-  surface_handler_->DecodeSurface(dec_surface);
-  return Status::kOk;
-}
-
-bool V4L2VideoDecoderDelegateH264Legacy::OutputPicture(
-    scoped_refptr<H264Picture> pic) {
-  surface_handler_->SurfaceReady(H264PictureToV4L2DecodeSurface(pic.get()),
-                                 pic->bitstream_id(), pic->visible_rect(),
-                                 pic->get_colorspace());
-  return true;
-}
-
-void V4L2VideoDecoderDelegateH264Legacy::Reset() {
-  num_slices_ = 0;
-  memset(&priv_->v4l2_decode_param, 0, sizeof(priv_->v4l2_decode_param));
-  memset(&priv_->v4l2_slice_params, 0, sizeof(priv_->v4l2_slice_params));
-}
-
-scoped_refptr<V4L2DecodeSurface>
-V4L2VideoDecoderDelegateH264Legacy::H264PictureToV4L2DecodeSurface(
-    H264Picture* pic) {
-  V4L2H264Picture* v4l2_pic = pic->AsV4L2H264Picture();
-  CHECK(v4l2_pic);
-  return v4l2_pic->dec_surface();
-}
-
-}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h b/media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h
deleted file mode 100644
index 802755e..0000000
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h
+++ /dev/null
@@ -1,81 +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.
-
-#ifndef MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_H264_LEGACY_H_
-#define MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_H264_LEGACY_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/scoped_refptr.h"
-#include "media/gpu/h264_decoder.h"
-#include "media/gpu/h264_dpb.h"
-
-namespace media {
-
-class V4L2Device;
-class V4L2DecodeSurface;
-class V4L2DecodeSurfaceHandler;
-struct V4L2VideoDecoderDelegateH264LegacyPrivate;
-
-class V4L2VideoDecoderDelegateH264Legacy : public H264Decoder::H264Accelerator {
- public:
-  using Status = H264Decoder::H264Accelerator::Status;
-
-  explicit V4L2VideoDecoderDelegateH264Legacy(
-      V4L2DecodeSurfaceHandler* surface_handler,
-      V4L2Device* device);
-
-  V4L2VideoDecoderDelegateH264Legacy(
-      const V4L2VideoDecoderDelegateH264Legacy&) = delete;
-  V4L2VideoDecoderDelegateH264Legacy& operator=(
-      const V4L2VideoDecoderDelegateH264Legacy&) = delete;
-
-  ~V4L2VideoDecoderDelegateH264Legacy() override;
-
-  // H264Decoder::H264Accelerator implementation.
-  scoped_refptr<H264Picture> CreateH264Picture() override;
-  Status SubmitFrameMetadata(const H264SPS* sps,
-                             const H264PPS* pps,
-                             const H264DPB& dpb,
-                             const H264Picture::Vector& ref_pic_listp0,
-                             const H264Picture::Vector& ref_pic_listb0,
-                             const H264Picture::Vector& ref_pic_listb1,
-                             scoped_refptr<H264Picture> pic) override;
-  Status SubmitSlice(const H264PPS* pps,
-                     const H264SliceHeader* slice_hdr,
-                     const H264Picture::Vector& ref_pic_list0,
-                     const H264Picture::Vector& ref_pic_list1,
-                     scoped_refptr<H264Picture> pic,
-                     const uint8_t* data,
-                     size_t size,
-                     const std::vector<SubsampleEntry>& subsamples) override;
-  Status SubmitDecode(scoped_refptr<H264Picture> pic) override;
-  bool OutputPicture(scoped_refptr<H264Picture> pic) override;
-  void Reset() override;
-
- private:
-  // Max size of reference list.
-  static constexpr size_t kDPBIndicesListSize = 32;
-
-  void H264PictureListToDPBIndicesList(const H264Picture::Vector& src_pic_list,
-                                       uint8_t dst_list[kDPBIndicesListSize]);
-  void H264DPBToV4L2DPB(
-      const H264DPB& dpb,
-      std::vector<scoped_refptr<V4L2DecodeSurface>>* ref_surfaces);
-  scoped_refptr<V4L2DecodeSurface> H264PictureToV4L2DecodeSurface(
-      H264Picture* pic);
-
-  size_t num_slices_;
-  V4L2DecodeSurfaceHandler* const surface_handler_;
-  V4L2Device* const device_;
-
-  // Contains the kernel-specific structures that we don't want to expose
-  // outside of the compilation unit.
-  const std::unique_ptr<V4L2VideoDecoderDelegateH264LegacyPrivate> priv_;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_H264_LEGACY_H_
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.cc
deleted file mode 100644
index c8dfebf..0000000
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.cc
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <linux/media/vp8-ctrls-legacy.h>
-#include <linux/videodev2.h>
-
-#include <type_traits>
-
-#include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
-#include "media/gpu/macros.h"
-#include "media/gpu/v4l2/v4l2_decode_surface.h"
-#include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
-#include "media/gpu/v4l2/v4l2_device.h"
-#include "media/gpu/vp8_picture.h"
-#include "media/parsers/vp8_parser.h"
-#include "v4l2_video_decoder_delegate_vp8_legacy.h"
-
-namespace media {
-namespace {
-
-void FillV4L2SegmentationHeader(const Vp8SegmentationHeader& vp8_sgmnt_hdr,
-                                struct v4l2_vp8_sgmnt_hdr* v4l2_sgmnt_hdr) {
-#define SET_V4L2_SGMNT_HDR_FLAG_IF(cond, flag) \
-  v4l2_sgmnt_hdr->flags |= ((vp8_sgmnt_hdr.cond) ? (flag) : 0)
-  SET_V4L2_SGMNT_HDR_FLAG_IF(segmentation_enabled,
-                             V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED);
-  SET_V4L2_SGMNT_HDR_FLAG_IF(update_mb_segmentation_map,
-                             V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP);
-  SET_V4L2_SGMNT_HDR_FLAG_IF(update_segment_feature_data,
-                             V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_FEATURE_DATA);
-#undef SET_V4L2_SGMNT_HDR_FLAG_IF
-  v4l2_sgmnt_hdr->segment_feature_mode = vp8_sgmnt_hdr.segment_feature_mode;
-
-  SafeArrayMemcpy(v4l2_sgmnt_hdr->quant_update,
-                  vp8_sgmnt_hdr.quantizer_update_value);
-  SafeArrayMemcpy(v4l2_sgmnt_hdr->lf_update, vp8_sgmnt_hdr.lf_update_value);
-  SafeArrayMemcpy(v4l2_sgmnt_hdr->segment_probs, vp8_sgmnt_hdr.segment_prob);
-}
-
-void FillV4L2LoopfilterHeader(const Vp8LoopFilterHeader& vp8_loopfilter_hdr,
-                              struct v4l2_vp8_loopfilter_hdr* v4l2_lf_hdr) {
-#define SET_V4L2_LF_HDR_FLAG_IF(cond, flag) \
-  v4l2_lf_hdr->flags |= ((vp8_loopfilter_hdr.cond) ? (flag) : 0)
-  SET_V4L2_LF_HDR_FLAG_IF(loop_filter_adj_enable, V4L2_VP8_LF_HDR_ADJ_ENABLE);
-  SET_V4L2_LF_HDR_FLAG_IF(mode_ref_lf_delta_update,
-                          V4L2_VP8_LF_HDR_DELTA_UPDATE);
-#undef SET_V4L2_LF_HDR_FLAG_IF
-
-#define LF_HDR_TO_V4L2_LF_HDR(a) v4l2_lf_hdr->a = vp8_loopfilter_hdr.a;
-  LF_HDR_TO_V4L2_LF_HDR(type);
-  LF_HDR_TO_V4L2_LF_HDR(level);
-  LF_HDR_TO_V4L2_LF_HDR(sharpness_level);
-#undef LF_HDR_TO_V4L2_LF_HDR
-
-  SafeArrayMemcpy(v4l2_lf_hdr->ref_frm_delta_magnitude,
-                  vp8_loopfilter_hdr.ref_frame_delta);
-  SafeArrayMemcpy(v4l2_lf_hdr->mb_mode_delta_magnitude,
-                  vp8_loopfilter_hdr.mb_mode_delta);
-}
-
-void FillV4L2QuantizationHeader(
-    const Vp8QuantizationHeader& vp8_quant_hdr,
-    struct v4l2_vp8_quantization_hdr* v4l2_quant_hdr) {
-  v4l2_quant_hdr->y_ac_qi = vp8_quant_hdr.y_ac_qi;
-  v4l2_quant_hdr->y_dc_delta = vp8_quant_hdr.y_dc_delta;
-  v4l2_quant_hdr->y2_dc_delta = vp8_quant_hdr.y2_dc_delta;
-  v4l2_quant_hdr->y2_ac_delta = vp8_quant_hdr.y2_ac_delta;
-  v4l2_quant_hdr->uv_dc_delta = vp8_quant_hdr.uv_dc_delta;
-  v4l2_quant_hdr->uv_ac_delta = vp8_quant_hdr.uv_ac_delta;
-}
-
-void FillV4L2Vp8EntropyHeader(const Vp8EntropyHeader& vp8_entropy_hdr,
-                              struct v4l2_vp8_entropy_hdr* v4l2_entropy_hdr) {
-  SafeArrayMemcpy(v4l2_entropy_hdr->coeff_probs, vp8_entropy_hdr.coeff_probs);
-  SafeArrayMemcpy(v4l2_entropy_hdr->y_mode_probs, vp8_entropy_hdr.y_mode_probs);
-  SafeArrayMemcpy(v4l2_entropy_hdr->uv_mode_probs,
-                  vp8_entropy_hdr.uv_mode_probs);
-  SafeArrayMemcpy(v4l2_entropy_hdr->mv_probs, vp8_entropy_hdr.mv_probs);
-}
-
-}  // namespace
-
-class V4L2VP8Picture : public VP8Picture {
- public:
-  explicit V4L2VP8Picture(scoped_refptr<V4L2DecodeSurface> dec_surface)
-      : dec_surface_(std::move(dec_surface)) {}
-
-  V4L2VP8Picture(const V4L2VP8Picture&) = delete;
-  V4L2VP8Picture& operator=(const V4L2VP8Picture&) = delete;
-
-  V4L2VP8Picture* AsV4L2VP8Picture() override { return this; }
-  scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
-
- private:
-  ~V4L2VP8Picture() override {}
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface_;
-};
-
-V4L2VideoDecoderDelegateVP8Legacy::V4L2VideoDecoderDelegateVP8Legacy(
-    V4L2DecodeSurfaceHandler* surface_handler,
-    V4L2Device* device)
-    : surface_handler_(surface_handler), device_(device) {
-  DCHECK(surface_handler_);
-}
-
-V4L2VideoDecoderDelegateVP8Legacy::~V4L2VideoDecoderDelegateVP8Legacy() {}
-
-scoped_refptr<VP8Picture>
-V4L2VideoDecoderDelegateVP8Legacy::CreateVP8Picture() {
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      surface_handler_->CreateSurface();
-  if (!dec_surface)
-    return nullptr;
-
-  return new V4L2VP8Picture(dec_surface);
-}
-
-bool V4L2VideoDecoderDelegateVP8Legacy::SubmitDecode(
-    scoped_refptr<VP8Picture> pic,
-    const Vp8ReferenceFrameVector& reference_frames) {
-  struct v4l2_ctrl_vp8_frame_hdr v4l2_frame_hdr;
-  memset(&v4l2_frame_hdr, 0, sizeof(v4l2_frame_hdr));
-
-  const auto& frame_hdr = pic->frame_hdr;
-  v4l2_frame_hdr.key_frame = frame_hdr->frame_type;
-#define FHDR_TO_V4L2_FHDR(a) v4l2_frame_hdr.a = frame_hdr->a
-  FHDR_TO_V4L2_FHDR(version);
-  FHDR_TO_V4L2_FHDR(width);
-  FHDR_TO_V4L2_FHDR(horizontal_scale);
-  FHDR_TO_V4L2_FHDR(height);
-  FHDR_TO_V4L2_FHDR(vertical_scale);
-  FHDR_TO_V4L2_FHDR(sign_bias_golden);
-  FHDR_TO_V4L2_FHDR(sign_bias_alternate);
-  FHDR_TO_V4L2_FHDR(prob_skip_false);
-  FHDR_TO_V4L2_FHDR(prob_intra);
-  FHDR_TO_V4L2_FHDR(prob_last);
-  FHDR_TO_V4L2_FHDR(prob_gf);
-  FHDR_TO_V4L2_FHDR(bool_dec_range);
-  FHDR_TO_V4L2_FHDR(bool_dec_value);
-  FHDR_TO_V4L2_FHDR(bool_dec_count);
-#undef FHDR_TO_V4L2_FHDR
-
-#define SET_V4L2_FRM_HDR_FLAG_IF(cond, flag) \
-  v4l2_frame_hdr.flags |= ((frame_hdr->cond) ? (flag) : 0)
-  SET_V4L2_FRM_HDR_FLAG_IF(is_experimental,
-                           V4L2_VP8_FRAME_HDR_FLAG_EXPERIMENTAL);
-  SET_V4L2_FRM_HDR_FLAG_IF(show_frame, V4L2_VP8_FRAME_HDR_FLAG_SHOW_FRAME);
-  SET_V4L2_FRM_HDR_FLAG_IF(mb_no_skip_coeff,
-                           V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF);
-#undef SET_V4L2_FRM_HDR_FLAG_IF
-
-  FillV4L2SegmentationHeader(frame_hdr->segmentation_hdr,
-                             &v4l2_frame_hdr.sgmnt_hdr);
-
-  FillV4L2LoopfilterHeader(frame_hdr->loopfilter_hdr, &v4l2_frame_hdr.lf_hdr);
-
-  FillV4L2QuantizationHeader(frame_hdr->quantization_hdr,
-                             &v4l2_frame_hdr.quant_hdr);
-
-  FillV4L2Vp8EntropyHeader(frame_hdr->entropy_hdr, &v4l2_frame_hdr.entropy_hdr);
-
-  v4l2_frame_hdr.first_part_size =
-      base::checked_cast<__u32>(frame_hdr->first_part_size);
-  v4l2_frame_hdr.first_part_offset =
-      base::checked_cast<__u32>(frame_hdr->first_part_offset);
-  v4l2_frame_hdr.macroblock_bit_offset =
-      base::checked_cast<__u32>(frame_hdr->macroblock_bit_offset);
-  v4l2_frame_hdr.num_dct_parts = frame_hdr->num_of_dct_partitions;
-
-  static_assert(std::extent<decltype(v4l2_frame_hdr.dct_part_sizes)>() ==
-                    std::extent<decltype(frame_hdr->dct_partition_sizes)>(),
-                "DCT partition size arrays must have equal number of elements");
-  for (size_t i = 0; i < frame_hdr->num_of_dct_partitions &&
-                     i < std::size(v4l2_frame_hdr.dct_part_sizes);
-       ++i)
-    v4l2_frame_hdr.dct_part_sizes[i] = frame_hdr->dct_partition_sizes[i];
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      VP8PictureToV4L2DecodeSurface(pic.get());
-  std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces;
-
-  const auto last_frame = reference_frames.GetFrame(Vp8RefType::VP8_FRAME_LAST);
-  if (last_frame) {
-    scoped_refptr<V4L2DecodeSurface> last_frame_surface =
-        VP8PictureToV4L2DecodeSurface(last_frame.get());
-    v4l2_frame_hdr.last_frame = last_frame_surface->GetReferenceID();
-    ref_surfaces.push_back(last_frame_surface);
-  } else {
-    v4l2_frame_hdr.last_frame = VIDEO_MAX_FRAME;
-  }
-
-  const auto golden_frame =
-      reference_frames.GetFrame(Vp8RefType::VP8_FRAME_GOLDEN);
-  if (golden_frame) {
-    scoped_refptr<V4L2DecodeSurface> golden_frame_surface =
-        VP8PictureToV4L2DecodeSurface(golden_frame.get());
-    v4l2_frame_hdr.golden_frame = golden_frame_surface->GetReferenceID();
-    ref_surfaces.push_back(golden_frame_surface);
-  } else {
-    v4l2_frame_hdr.golden_frame = VIDEO_MAX_FRAME;
-  }
-
-  const auto alt_frame =
-      reference_frames.GetFrame(Vp8RefType::VP8_FRAME_ALTREF);
-  if (alt_frame) {
-    scoped_refptr<V4L2DecodeSurface> alt_frame_surface =
-        VP8PictureToV4L2DecodeSurface(alt_frame.get());
-    v4l2_frame_hdr.alt_frame = alt_frame_surface->GetReferenceID();
-    ref_surfaces.push_back(alt_frame_surface);
-  } else {
-    v4l2_frame_hdr.alt_frame = VIDEO_MAX_FRAME;
-  }
-
-  struct v4l2_ext_control ctrl;
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR;
-  ctrl.size = sizeof(v4l2_frame_hdr);
-  ctrl.ptr = &v4l2_frame_hdr;
-
-  struct v4l2_ext_controls ext_ctrls;
-  memset(&ext_ctrls, 0, sizeof(ext_ctrls));
-  ext_ctrls.count = 1;
-  ext_ctrls.controls = &ctrl;
-  dec_surface->PrepareSetCtrls(&ext_ctrls);
-  if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
-    VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
-    return false;
-  }
-
-  dec_surface->SetReferenceSurfaces(ref_surfaces);
-
-  if (!surface_handler_->SubmitSlice(dec_surface.get(), frame_hdr->data,
-                                     frame_hdr->frame_size))
-    return false;
-
-  DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
-  surface_handler_->DecodeSurface(dec_surface);
-  return true;
-}
-
-bool V4L2VideoDecoderDelegateVP8Legacy::OutputPicture(
-    scoped_refptr<VP8Picture> pic) {
-  surface_handler_->SurfaceReady(VP8PictureToV4L2DecodeSurface(pic.get()),
-                                 pic->bitstream_id(), pic->visible_rect(),
-                                 pic->get_colorspace());
-  return true;
-}
-
-scoped_refptr<V4L2DecodeSurface>
-V4L2VideoDecoderDelegateVP8Legacy::VP8PictureToV4L2DecodeSurface(
-    VP8Picture* pic) {
-  V4L2VP8Picture* v4l2_pic = pic->AsV4L2VP8Picture();
-  CHECK(v4l2_pic);
-  return v4l2_pic->dec_surface();
-}
-
-}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.h b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.h
deleted file mode 100644
index dd623cd..0000000
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8_legacy.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_VP8_LEGACY_H_
-#define MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_VP8_LEGACY_H_
-
-#include "base/memory/scoped_refptr.h"
-#include "media/gpu/vp8_decoder.h"
-
-namespace media {
-
-class V4L2Device;
-class V4L2DecodeSurface;
-class V4L2DecodeSurfaceHandler;
-
-class V4L2VideoDecoderDelegateVP8Legacy : public VP8Decoder::VP8Accelerator {
- public:
-  explicit V4L2VideoDecoderDelegateVP8Legacy(
-      V4L2DecodeSurfaceHandler* surface_handler,
-      V4L2Device* device);
-
-  V4L2VideoDecoderDelegateVP8Legacy(const V4L2VideoDecoderDelegateVP8Legacy&) =
-      delete;
-  V4L2VideoDecoderDelegateVP8Legacy& operator=(
-      const V4L2VideoDecoderDelegateVP8Legacy&) = delete;
-
-  ~V4L2VideoDecoderDelegateVP8Legacy() override;
-
-  // VP8Decoder::VP8Accelerator implementation.
-  scoped_refptr<VP8Picture> CreateVP8Picture() override;
-  bool SubmitDecode(scoped_refptr<VP8Picture> pic,
-                    const Vp8ReferenceFrameVector& reference_frames) override;
-  bool OutputPicture(scoped_refptr<VP8Picture> pic) override;
-
- private:
-  scoped_refptr<V4L2DecodeSurface> VP8PictureToV4L2DecodeSurface(
-      VP8Picture* pic);
-
-  V4L2DecodeSurfaceHandler* const surface_handler_;
-  V4L2Device* const device_;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_VP8_LEGACY_H_
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.cc
deleted file mode 100644
index a8d06890..0000000
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.cc
+++ /dev/null
@@ -1,430 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.h"
-
-// Must come before linux/media/vp9-ctrls-legacy.h.
-#include <linux/videodev2.h>
-
-#include <linux/media/vp9-ctrls-legacy.h>
-#include <string.h>
-
-#include <type_traits>
-
-#include "base/logging.h"
-#include "media/gpu/macros.h"
-#include "media/gpu/v4l2/v4l2_decode_surface.h"
-#include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
-#include "media/gpu/v4l2/v4l2_device.h"
-#include "media/gpu/vp9_picture.h"
-
-namespace media {
-
-using DecodeStatus = VP9Decoder::VP9Accelerator::Status;
-
-namespace {
-
-void FillV4L2VP9LoopFilterParams(
-    const Vp9LoopFilterParams& vp9_lf_params,
-    struct v4l2_vp9_loop_filter_params* v4l2_lf_params) {
-#define SET_LF_PARAMS_FLAG_IF(cond, flag) \
-  v4l2_lf_params->flags |= ((vp9_lf_params.cond) ? (flag) : 0)
-  SET_LF_PARAMS_FLAG_IF(delta_enabled, V4L2_VP9_LOOP_FLTR_FLAG_DELTA_ENABLED);
-  SET_LF_PARAMS_FLAG_IF(delta_update, V4L2_VP9_LOOP_FLTR_FLAG_DELTA_UPDATE);
-#undef SET_LF_PARAMS_FLAG_IF
-
-  v4l2_lf_params->level = vp9_lf_params.level;
-  v4l2_lf_params->sharpness = vp9_lf_params.sharpness;
-
-  SafeArrayMemcpy(v4l2_lf_params->deltas, vp9_lf_params.ref_deltas);
-  SafeArrayMemcpy(v4l2_lf_params->mode_deltas, vp9_lf_params.mode_deltas);
-  SafeArrayMemcpy(v4l2_lf_params->lvl_lookup, vp9_lf_params.lvl);
-}
-
-void FillV4L2VP9QuantizationParams(
-    const Vp9QuantizationParams& vp9_quant_params,
-    struct v4l2_vp9_quantization_params* v4l2_q_params) {
-#define SET_Q_PARAMS_FLAG_IF(cond, flag) \
-  v4l2_q_params->flags |= ((vp9_quant_params.cond) ? (flag) : 0)
-  SET_Q_PARAMS_FLAG_IF(IsLossless(), V4L2_VP9_QUANT_PARAMS_FLAG_LOSSLESS);
-#undef SET_Q_PARAMS_FLAG_IF
-
-#define Q_PARAMS_TO_V4L2_Q_PARAMS(a) v4l2_q_params->a = vp9_quant_params.a
-  Q_PARAMS_TO_V4L2_Q_PARAMS(base_q_idx);
-  Q_PARAMS_TO_V4L2_Q_PARAMS(delta_q_y_dc);
-  Q_PARAMS_TO_V4L2_Q_PARAMS(delta_q_uv_dc);
-  Q_PARAMS_TO_V4L2_Q_PARAMS(delta_q_uv_ac);
-#undef Q_PARAMS_TO_V4L2_Q_PARAMS
-}
-
-void FillV4L2VP9SegmentationParams(
-    const Vp9SegmentationParams& vp9_segm_params,
-    struct v4l2_vp9_segmentation_params* v4l2_segm_params) {
-#define SET_SEG_PARAMS_FLAG_IF(cond, flag) \
-  v4l2_segm_params->flags |= ((vp9_segm_params.cond) ? (flag) : 0)
-  SET_SEG_PARAMS_FLAG_IF(enabled, V4L2_VP9_SGMNT_PARAM_FLAG_ENABLED);
-  SET_SEG_PARAMS_FLAG_IF(update_map, V4L2_VP9_SGMNT_PARAM_FLAG_UPDATE_MAP);
-  SET_SEG_PARAMS_FLAG_IF(temporal_update,
-                         V4L2_VP9_SGMNT_PARAM_FLAG_TEMPORAL_UPDATE);
-  SET_SEG_PARAMS_FLAG_IF(update_data, V4L2_VP9_SGMNT_PARAM_FLAG_UPDATE_DATA);
-  SET_SEG_PARAMS_FLAG_IF(abs_or_delta_update,
-                         V4L2_VP9_SGMNT_PARAM_FLAG_ABS_OR_DELTA_UPDATE);
-#undef SET_SEG_PARAMS_FLAG_IF
-
-  SafeArrayMemcpy(v4l2_segm_params->tree_probs, vp9_segm_params.tree_probs);
-  SafeArrayMemcpy(v4l2_segm_params->pred_probs, vp9_segm_params.pred_probs);
-  SafeArrayMemcpy(v4l2_segm_params->feature_data, vp9_segm_params.feature_data);
-
-  static_assert(
-      std::extent<decltype(v4l2_segm_params->feature_enabled)>() ==
-              std::extent<decltype(vp9_segm_params.feature_enabled)>() &&
-          std::extent<decltype(v4l2_segm_params->feature_enabled[0])>() ==
-              std::extent<decltype(vp9_segm_params.feature_enabled[0])>(),
-      "feature_enabled arrays must be of same size");
-  for (size_t i = 0; i < std::size(v4l2_segm_params->feature_enabled); ++i) {
-    for (size_t j = 0; j < std::size(v4l2_segm_params->feature_enabled[i]);
-         ++j) {
-      v4l2_segm_params->feature_enabled[i][j] =
-          vp9_segm_params.feature_enabled[i][j];
-    }
-  }
-}
-
-void FillV4L2Vp9EntropyContext(const Vp9FrameContext& vp9_frame_ctx,
-                               struct v4l2_vp9_entropy_ctx* v4l2_entropy_ctx) {
-#define ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(a) \
-  SafeArrayMemcpy(v4l2_entropy_ctx->a, vp9_frame_ctx.a)
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(tx_probs_8x8);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(tx_probs_16x16);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(tx_probs_32x32);
-
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(coef_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(skip_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(inter_mode_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(interp_filter_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(is_inter_prob);
-
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(comp_mode_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(single_ref_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(comp_ref_prob);
-
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(y_mode_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(uv_mode_probs);
-
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(partition_probs);
-
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_joint_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_sign_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class0_bit_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_bits_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class0_fr_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_fr_probs);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class0_hp_prob);
-  ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_hp_prob);
-#undef ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR
-}
-
-void FillVp9FrameContext(struct v4l2_vp9_entropy_ctx& v4l2_entropy_ctx,
-                         Vp9FrameContext* vp9_frame_ctx) {
-#define ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(a) \
-  SafeArrayMemcpy(vp9_frame_ctx->a, v4l2_entropy_ctx.a)
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(tx_probs_8x8);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(tx_probs_16x16);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(tx_probs_32x32);
-
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(coef_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(skip_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(inter_mode_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(interp_filter_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(is_inter_prob);
-
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(comp_mode_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(single_ref_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(comp_ref_prob);
-
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(y_mode_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(uv_mode_probs);
-
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(partition_probs);
-
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_joint_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_sign_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class0_bit_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_bits_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class0_fr_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_fr_probs);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class0_hp_prob);
-  ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_hp_prob);
-#undef ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX
-}
-
-}  // namespace
-
-class V4L2VP9Picture : public VP9Picture {
- public:
-  explicit V4L2VP9Picture(scoped_refptr<V4L2DecodeSurface> dec_surface)
-      : dec_surface_(std::move(dec_surface)) {}
-
-  V4L2VP9Picture(const V4L2VP9Picture&) = delete;
-  V4L2VP9Picture& operator=(const V4L2VP9Picture&) = delete;
-
-  V4L2VP9Picture* AsV4L2VP9Picture() override { return this; }
-  scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
-
- private:
-  ~V4L2VP9Picture() override {}
-
-  scoped_refptr<VP9Picture> CreateDuplicate() override {
-    return new V4L2VP9Picture(dec_surface_);
-  }
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface_;
-};
-
-V4L2VideoDecoderDelegateVP9Legacy::V4L2VideoDecoderDelegateVP9Legacy(
-    V4L2DecodeSurfaceHandler* surface_handler,
-    V4L2Device* device)
-    : surface_handler_(surface_handler),
-      device_(device),
-      device_needs_compressed_header_parsed_(
-          device->IsCtrlExposed(V4L2_CID_MPEG_VIDEO_VP9_ENTROPY)) {
-  DCHECK(surface_handler_);
-
-  DVLOG_IF(1, device_needs_compressed_header_parsed_)
-      << "Device requires frame context parsing";
-}
-
-V4L2VideoDecoderDelegateVP9Legacy::~V4L2VideoDecoderDelegateVP9Legacy() {}
-
-scoped_refptr<VP9Picture>
-V4L2VideoDecoderDelegateVP9Legacy::CreateVP9Picture() {
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      surface_handler_->CreateSurface();
-  if (!dec_surface)
-    return nullptr;
-
-  return new V4L2VP9Picture(std::move(dec_surface));
-}
-
-DecodeStatus V4L2VideoDecoderDelegateVP9Legacy::SubmitDecode(
-    scoped_refptr<VP9Picture> pic,
-    const Vp9SegmentationParams& segm_params,
-    const Vp9LoopFilterParams& lf_params,
-    const Vp9ReferenceFrameVector& ref_frames,
-    base::OnceClosure done_cb) {
-  const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get();
-  DCHECK(frame_hdr);
-
-  struct v4l2_ctrl_vp9_frame_hdr v4l2_frame_hdr;
-  memset(&v4l2_frame_hdr, 0, sizeof(v4l2_frame_hdr));
-
-#define FHDR_TO_V4L2_FHDR(a) v4l2_frame_hdr.a = frame_hdr->a
-  FHDR_TO_V4L2_FHDR(profile);
-  FHDR_TO_V4L2_FHDR(frame_type);
-
-  FHDR_TO_V4L2_FHDR(bit_depth);
-  FHDR_TO_V4L2_FHDR(color_range);
-  FHDR_TO_V4L2_FHDR(subsampling_x);
-  FHDR_TO_V4L2_FHDR(subsampling_y);
-
-  FHDR_TO_V4L2_FHDR(frame_width);
-  FHDR_TO_V4L2_FHDR(frame_height);
-  FHDR_TO_V4L2_FHDR(render_width);
-  FHDR_TO_V4L2_FHDR(render_height);
-
-  FHDR_TO_V4L2_FHDR(reset_frame_context);
-
-  FHDR_TO_V4L2_FHDR(interpolation_filter);
-  FHDR_TO_V4L2_FHDR(frame_context_idx);
-
-  FHDR_TO_V4L2_FHDR(tile_cols_log2);
-  FHDR_TO_V4L2_FHDR(tile_rows_log2);
-
-  FHDR_TO_V4L2_FHDR(header_size_in_bytes);
-#undef FHDR_TO_V4L2_FHDR
-  v4l2_frame_hdr.color_space = static_cast<uint8_t>(frame_hdr->color_space);
-
-  FillV4L2VP9QuantizationParams(frame_hdr->quant_params,
-                                &v4l2_frame_hdr.quant_params);
-
-#define SET_V4L2_FRM_HDR_FLAG_IF(cond, flag) \
-  v4l2_frame_hdr.flags |= ((frame_hdr->cond) ? (flag) : 0)
-  SET_V4L2_FRM_HDR_FLAG_IF(show_frame, V4L2_VP9_FRAME_HDR_FLAG_SHOW_FRAME);
-  SET_V4L2_FRM_HDR_FLAG_IF(error_resilient_mode,
-                           V4L2_VP9_FRAME_HDR_FLAG_ERR_RES);
-  SET_V4L2_FRM_HDR_FLAG_IF(intra_only, V4L2_VP9_FRAME_HDR_FLAG_FRAME_INTRA);
-  SET_V4L2_FRM_HDR_FLAG_IF(allow_high_precision_mv,
-                           V4L2_VP9_FRAME_HDR_ALLOW_HIGH_PREC_MV);
-  SET_V4L2_FRM_HDR_FLAG_IF(refresh_frame_context,
-                           V4L2_VP9_FRAME_HDR_REFRESH_FRAME_CTX);
-  SET_V4L2_FRM_HDR_FLAG_IF(frame_parallel_decoding_mode,
-                           V4L2_VP9_FRAME_HDR_PARALLEL_DEC_MODE);
-#undef SET_V4L2_FRM_HDR_FLAG_IF
-
-  FillV4L2VP9LoopFilterParams(lf_params, &v4l2_frame_hdr.lf_params);
-  FillV4L2VP9SegmentationParams(segm_params, &v4l2_frame_hdr.sgmnt_params);
-
-  std::vector<struct v4l2_ext_control> ctrls;
-
-  struct v4l2_ext_control ctrl;
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_FRAME_HDR;
-  ctrl.size = sizeof(v4l2_frame_hdr);
-  ctrl.ptr = &v4l2_frame_hdr;
-  ctrls.push_back(ctrl);
-
-  struct v4l2_ctrl_vp9_decode_param v4l2_decode_param;
-  memset(&v4l2_decode_param, 0, sizeof(v4l2_decode_param));
-  DCHECK_EQ(kVp9NumRefFrames, std::size(v4l2_decode_param.ref_frames));
-
-  std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces;
-  for (size_t i = 0; i < kVp9NumRefFrames; ++i) {
-    auto ref_pic = ref_frames.GetFrame(i);
-    if (ref_pic) {
-      scoped_refptr<V4L2DecodeSurface> ref_surface =
-          VP9PictureToV4L2DecodeSurface(ref_pic.get());
-
-      v4l2_decode_param.ref_frames[i] = ref_surface->GetReferenceID();
-      ref_surfaces.push_back(ref_surface);
-    } else {
-      v4l2_decode_param.ref_frames[i] = VIDEO_MAX_FRAME;
-    }
-  }
-
-  static_assert(std::extent<decltype(v4l2_decode_param.active_ref_frames)>() ==
-                    std::extent<decltype(frame_hdr->ref_frame_idx)>(),
-                "active reference frame array sizes mismatch");
-
-  for (size_t i = 0; i < std::size(frame_hdr->ref_frame_idx); ++i) {
-    uint8_t idx = frame_hdr->ref_frame_idx[i];
-    if (idx >= kVp9NumRefFrames)
-      return DecodeStatus::kFail;
-
-    struct v4l2_vp9_reference_frame* v4l2_ref_frame =
-        &v4l2_decode_param.active_ref_frames[i];
-
-    scoped_refptr<VP9Picture> ref_pic = ref_frames.GetFrame(idx);
-    if (ref_pic) {
-      scoped_refptr<V4L2DecodeSurface> ref_surface =
-          VP9PictureToV4L2DecodeSurface(ref_pic.get());
-      v4l2_ref_frame->buf_index = ref_surface->GetReferenceID();
-#define REF_TO_V4L2_REF(a) v4l2_ref_frame->a = ref_pic->frame_hdr->a
-      REF_TO_V4L2_REF(frame_width);
-      REF_TO_V4L2_REF(frame_height);
-      REF_TO_V4L2_REF(bit_depth);
-      REF_TO_V4L2_REF(subsampling_x);
-      REF_TO_V4L2_REF(subsampling_y);
-#undef REF_TO_V4L2_REF
-    } else {
-      v4l2_ref_frame->buf_index = VIDEO_MAX_FRAME;
-    }
-  }
-
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_DECODE_PARAM;
-  ctrl.size = sizeof(v4l2_decode_param);
-  ctrl.ptr = &v4l2_decode_param;
-  ctrls.push_back(ctrl);
-
-  // Defined outside of the if() clause below as it must remain valid until
-  // the call to SubmitExtControls().
-  struct v4l2_ctrl_vp9_entropy v4l2_entropy;
-  if (device_needs_compressed_header_parsed_) {
-    memset(&v4l2_entropy, 0, sizeof(v4l2_entropy));
-    FillV4L2Vp9EntropyContext(frame_hdr->initial_frame_context,
-                              &v4l2_entropy.initial_entropy_ctx);
-    FillV4L2Vp9EntropyContext(frame_hdr->frame_context,
-                              &v4l2_entropy.current_entropy_ctx);
-    v4l2_entropy.tx_mode = frame_hdr->compressed_header.tx_mode;
-    v4l2_entropy.reference_mode = frame_hdr->compressed_header.reference_mode;
-
-    memset(&ctrl, 0, sizeof(ctrl));
-    ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_ENTROPY;
-    ctrl.size = sizeof(v4l2_entropy);
-    ctrl.ptr = &v4l2_entropy;
-    ctrls.push_back(ctrl);
-  }
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      VP9PictureToV4L2DecodeSurface(pic.get());
-
-  struct v4l2_ext_controls ext_ctrls;
-  memset(&ext_ctrls, 0, sizeof(ext_ctrls));
-  ext_ctrls.count = ctrls.size();
-  ext_ctrls.controls = &ctrls[0];
-  dec_surface->PrepareSetCtrls(&ext_ctrls);
-  if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
-    VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
-    return DecodeStatus::kFail;
-  }
-
-  dec_surface->SetReferenceSurfaces(ref_surfaces);
-  dec_surface->SetDecodeDoneCallback(std::move(done_cb));
-
-  if (!surface_handler_->SubmitSlice(dec_surface.get(), frame_hdr->data,
-                                     frame_hdr->frame_size))
-    return DecodeStatus::kFail;
-
-  DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
-  surface_handler_->DecodeSurface(dec_surface);
-  return DecodeStatus::kOk;
-}
-
-bool V4L2VideoDecoderDelegateVP9Legacy::OutputPicture(
-    scoped_refptr<VP9Picture> pic) {
-  surface_handler_->SurfaceReady(VP9PictureToV4L2DecodeSurface(pic.get()),
-                                 pic->bitstream_id(), pic->visible_rect(),
-                                 pic->get_colorspace());
-  return true;
-}
-
-bool V4L2VideoDecoderDelegateVP9Legacy::GetFrameContext(
-    scoped_refptr<VP9Picture> pic,
-    Vp9FrameContext* frame_ctx) {
-  struct v4l2_ctrl_vp9_entropy v4l2_entropy;
-  memset(&v4l2_entropy, 0, sizeof(v4l2_entropy));
-
-  struct v4l2_ext_control ctrl;
-  memset(&ctrl, 0, sizeof(ctrl));
-  ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_ENTROPY;
-  ctrl.size = sizeof(v4l2_entropy);
-  ctrl.ptr = &v4l2_entropy;
-
-  scoped_refptr<V4L2DecodeSurface> dec_surface =
-      VP9PictureToV4L2DecodeSurface(pic.get());
-
-  struct v4l2_ext_controls ext_ctrls;
-  memset(&ext_ctrls, 0, sizeof(ext_ctrls));
-  ext_ctrls.count = 1;
-  ext_ctrls.controls = &ctrl;
-  dec_surface->PrepareSetCtrls(&ext_ctrls);
-  if (device_->Ioctl(VIDIOC_G_EXT_CTRLS, &ext_ctrls) != 0) {
-    VPLOGF(1) << "ioctl() failed: VIDIOC_G_EXT_CTRLS";
-    return false;
-  }
-
-  FillVp9FrameContext(v4l2_entropy.current_entropy_ctx, frame_ctx);
-  return true;
-}
-
-bool V4L2VideoDecoderDelegateVP9Legacy::NeedsCompressedHeaderParsed() const {
-  return device_needs_compressed_header_parsed_;
-}
-
-bool V4L2VideoDecoderDelegateVP9Legacy::SupportsContextProbabilityReadback()
-    const {
-  return true;
-}
-
-scoped_refptr<V4L2DecodeSurface>
-V4L2VideoDecoderDelegateVP9Legacy::VP9PictureToV4L2DecodeSurface(
-    VP9Picture* pic) {
-  V4L2VP9Picture* v4l2_pic = pic->AsV4L2VP9Picture();
-  CHECK(v4l2_pic);
-  return v4l2_pic->dec_surface();
-}
-
-}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.h b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.h
deleted file mode 100644
index 7efe7bb..0000000
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9_legacy.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_VP9_LEGACY_H_
-#define MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_VP9_LEGACY_H_
-
-#include "base/callback.h"
-#include "base/memory/scoped_refptr.h"
-#include "media/filters/vp9_parser.h"
-#include "media/gpu/vp9_decoder.h"
-
-namespace media {
-
-class V4L2DecodeSurface;
-class V4L2DecodeSurfaceHandler;
-class V4L2Device;
-
-class V4L2VideoDecoderDelegateVP9Legacy : public VP9Decoder::VP9Accelerator {
- public:
-  explicit V4L2VideoDecoderDelegateVP9Legacy(
-      V4L2DecodeSurfaceHandler* surface_handler,
-      V4L2Device* device);
-
-  V4L2VideoDecoderDelegateVP9Legacy(const V4L2VideoDecoderDelegateVP9Legacy&) =
-      delete;
-  V4L2VideoDecoderDelegateVP9Legacy& operator=(
-      const V4L2VideoDecoderDelegateVP9Legacy&) = delete;
-
-  ~V4L2VideoDecoderDelegateVP9Legacy() override;
-
-  // VP9Decoder::VP9Accelerator implementation.
-  scoped_refptr<VP9Picture> CreateVP9Picture() override;
-
-  Status SubmitDecode(scoped_refptr<VP9Picture> pic,
-                      const Vp9SegmentationParams& segm_params,
-                      const Vp9LoopFilterParams& lf_params,
-                      const Vp9ReferenceFrameVector& reference_frames,
-                      base::OnceClosure done_cb) override;
-
-  bool OutputPicture(scoped_refptr<VP9Picture> pic) override;
-
-  bool GetFrameContext(scoped_refptr<VP9Picture> pic,
-                       Vp9FrameContext* frame_ctx) override;
-
-  bool NeedsCompressedHeaderParsed() const override;
-  bool SupportsContextProbabilityReadback() const override;
-
- private:
-  scoped_refptr<V4L2DecodeSurface> VP9PictureToV4L2DecodeSurface(
-      VP9Picture* pic);
-
-  V4L2DecodeSurfaceHandler* const surface_handler_;
-  V4L2Device* const device_;
-
-  // True if |device_| exposes the V4L2_CID_STATELESS_VP9_FRAME control
-  // (indicating that the driver needs the entropy tables from the compressed
-  // header).
-  const bool device_needs_compressed_header_parsed_;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_V4L2_V4L2_VIDEO_DECODER_DELEGATE_VP9_LEGACY_H_
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 0a5f457..38425f4 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -162,13 +162,33 @@
   return false;
 }
 
-size_t NameValueSizeBytes(const std::string& name, const std::string& value) {
-  base::CheckedNumeric<size_t> name_value_pair_size = name.size();
-  name_value_pair_size += value.size();
+size_t NameValueSizeBytes(const net::CanonicalCookie& cc) {
+  base::CheckedNumeric<size_t> name_value_pair_size = cc.Name().size();
+  name_value_pair_size += cc.Value().size();
   DCHECK(name_value_pair_size.IsValid());
   return name_value_pair_size.ValueOrDie();
 }
 
+size_t NumBytesInCookieMapForKey(
+    const net::CookieMonster::CookieMap& cookie_map,
+    const std::string& key) {
+  size_t result = 0;
+  auto range = cookie_map.equal_range(key);
+  for (auto it = range.first; it != range.second; ++it) {
+    result += NameValueSizeBytes(*it->second);
+  }
+  return result;
+}
+
+size_t NumBytesInCookieItVector(
+    const net::CookieMonster::CookieItVector& cookie_its) {
+  size_t result = 0;
+  for (const auto& it : cookie_its) {
+    result += NameValueSizeBytes(*it->second);
+  }
+  return result;
+}
+
 }  // namespace
 
 namespace net {
@@ -182,7 +202,8 @@
 
 const size_t CookieMonster::kMaxDomainPurgedKeys = 100;
 
-const size_t CookieMonster::kPerPartitionDomainMaxCookies = 10;
+const size_t CookieMonster::kPerPartitionDomainMaxCookieBytes = 10240;
+const size_t CookieMonster::kPerPartitionDomainMaxCookies = 180;
 
 const size_t CookieMonster::kDomainCookiesQuotaLow = 30;
 const size_t CookieMonster::kDomainCookiesQuotaMedium = 50;
@@ -1560,7 +1581,7 @@
 
     if (cc->IsEffectivelySameSiteNone()) {
       UMA_HISTOGRAM_COUNTS_10000("Cookie.SameSiteNoneSizeBytes",
-                                 NameValueSizeBytes(cc->Name(), cc->Value()));
+                                 NameValueSizeBytes(*cc));
     }
 
     bool is_partitioned_cookie = cc->IsPartitioned();
@@ -1981,7 +2002,9 @@
   if (cookie_partition_it == partitioned_cookies_.end())
     return num_deleted;
 
-  if (cookie_partition_it->second->count(key) > kPerPartitionDomainMaxCookies) {
+  if (NumBytesInCookieMapForKey(*cookie_partition_it->second.get(), key) >
+          kPerPartitionDomainMaxCookieBytes ||
+      cookie_partition_it->second->count(key) > kPerPartitionDomainMaxCookies) {
     // TODO(crbug.com/1225444): Log garbage collection for partitioned cookies.
 
     CookieItVector non_expired_cookie_its;
@@ -1989,18 +2012,20 @@
         current, cookie_partition_it,
         cookie_partition_it->second->equal_range(key), &non_expired_cookie_its);
 
-    if (non_expired_cookie_its.size() > kPerPartitionDomainMaxCookies) {
+    size_t bytes_used = NumBytesInCookieItVector(non_expired_cookie_its);
+
+    if (bytes_used > kPerPartitionDomainMaxCookieBytes ||
+        non_expired_cookie_its.size() > kPerPartitionDomainMaxCookies) {
       // TODO(crbug.com/1225444): Log deep garbage collection for partitioned
       // cookies.
-
-      // For now, just delete the least recently accessed partition cookies
-      // until we are under the per-partition domain limit. All partitioned
-      // cookies are Secure since they require the __Host- prefix.
       std::sort(non_expired_cookie_its.begin(), non_expired_cookie_its.end(),
                 LRACookieSorter);
+
       for (size_t i = 0;
-           i < (non_expired_cookie_its.size() - kPerPartitionDomainMaxCookies);
+           bytes_used > kPerPartitionDomainMaxCookieBytes ||
+           non_expired_cookie_its.size() - i > kPerPartitionDomainMaxCookies;
            ++i) {
+        bytes_used -= NameValueSizeBytes(*non_expired_cookie_its[i]->second);
         InternalDeletePartitionedCookie(
             cookie_partition_it, non_expired_cookie_its[i], true,
             DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN);
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 227ca62..900d6fc 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -139,6 +139,7 @@
   static const size_t kMaxDomainPurgedKeys;
 
   // Partitioned cookie garbage collection thresholds.
+  static const size_t kPerPartitionDomainMaxCookieBytes;
   static const size_t kPerPartitionDomainMaxCookies;
   // TODO(crbug.com/1225444): Add global limit to number of partitioned cookies.
 
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 9fd9da3..f05c33e 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -923,29 +923,6 @@
                            70U);
   }
 
-  // Test enforcement of the per-partition domain limit on partitioned cookies.
-  void TestPartitionedCookiesGarbageCollectionHelper() {
-    DCHECK_EQ(10u, CookieMonster::kPerPartitionDomainMaxCookies);
-    int max_cookies = CookieMonster::kPerPartitionDomainMaxCookies;
-    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
-
-    auto cookie_partition_key =
-        CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
-    for (int i = 0; i < max_cookies + 5; ++i) {
-      std::string cookie = base::StringPrintf("__Host-a%02d=b", i);
-      EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(),
-                            cookie + "; secure; path=/; partitioned",
-                            cookie_partition_key));
-      std::string cookies =
-          this->GetCookies(cm.get(), https_www_foo_.url(),
-                           CookiePartitionKeyCollection(cookie_partition_key));
-      EXPECT_NE(cookies.find(cookie), std::string::npos);
-      EXPECT_LE(CountInString(cookies, '='), max_cookies);
-    }
-    // TODO(crbug.com/1225444): Test recording stats for deleting partitioned
-    // cookies.
-  }
-
   // Function for creating a CM with a number of cookies in it,
   // no store (and hence no ability to affect access time).
   std::unique_ptr<CookieMonster> CreateMonsterForGC(int num_cookies) {
@@ -1780,8 +1757,64 @@
   TestPriorityAwareGarbageCollectHelperMixed();
 }
 
-TEST_F(CookieMonsterTest, TestPartitionedCookiesGarbageCollection) {
-  TestPartitionedCookiesGarbageCollectionHelper();
+TEST_F(CookieMonsterTest, TestPartitionedCookiesGarbageCollection_Memory) {
+  // Limit should be 10 KB.
+  DCHECK_EQ(1024u * 10u, CookieMonster::kPerPartitionDomainMaxCookieBytes);
+
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cookie_partition_key =
+      CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite1.com"));
+
+  for (size_t i = 0; i < 41; ++i) {
+    std::string cookie_value((10240 / 40) - (i < 10 ? 1 : 2), '0');
+    std::string cookie =
+        base::StrCat({base::NumberToString(i), "=", cookie_value});
+    EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(),
+                          cookie + "; secure; path=/; partitioned",
+                          cookie_partition_key))
+        << "Failed to set cookie " << i;
+  }
+
+  std::string cookies =
+      this->GetCookies(cm.get(), https_www_foo_.url(),
+                       CookiePartitionKeyCollection(cookie_partition_key));
+
+  EXPECT_THAT(cookies, CookieStringIs(
+                           testing::Not(testing::Contains(testing::Key("0")))));
+  for (size_t i = 1; i < 41; ++i) {
+    EXPECT_THAT(cookies, CookieStringIs(testing::Contains(
+                             testing::Key(base::NumberToString(i)))))
+        << "Failed to find cookie " << i;
+  }
+}
+
+TEST_F(CookieMonsterTest, TestPartitionedCookiesGarbageCollection_MaxCookies) {
+  // Partitioned cookies also limit domains to 180 cookies per partition.
+  DCHECK_EQ(180u, CookieMonster::kPerPartitionDomainMaxCookies);
+
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cookie_partition_key =
+      CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
+
+  for (size_t i = 0; i < 181; ++i) {
+    std::string cookie = base::StrCat({base::NumberToString(i), "=0"});
+    EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(),
+                          cookie + "; secure; path=/; partitioned",
+                          cookie_partition_key))
+        << "Failed to set cookie " << i;
+  }
+
+  std::string cookies =
+      this->GetCookies(cm.get(), https_www_foo_.url(),
+                       CookiePartitionKeyCollection(cookie_partition_key));
+  EXPECT_THAT(cookies, CookieStringIs(
+                           testing::Not(testing::Contains(testing::Key("0")))));
+  for (size_t i = 1; i < 181; ++i) {
+    std::string cookie = base::StrCat({base::NumberToString(i), "=0"});
+    EXPECT_THAT(cookies, CookieStringIs(testing::Contains(
+                             testing::Key(base::NumberToString(i)))))
+        << "Failed to find cookie " << i;
+  }
 }
 
 TEST_F(CookieMonsterTest, SetCookieableSchemes) {
diff --git a/pdf/pdfium/findtext_unittest.cc b/pdf/pdfium/findtext_unittest.cc
index e9706ed..890aaf9 100644
--- a/pdf/pdfium/findtext_unittest.cc
+++ b/pdf/pdfium/findtext_unittest.cc
@@ -73,8 +73,6 @@
 
   EXPECT_CALL(client,
               NotifyNumberOfFindResultsChanged(1, /*final_result=*/false));
-  EXPECT_CALL(client,
-              NotifySelectedFindResultChanged(0, /*final_result=*/false));
   for (int i = 2; i < count + 1; ++i) {
     EXPECT_CALL(client,
                 NotifyNumberOfFindResultsChanged(i, /*final_result=*/false));
@@ -184,6 +182,7 @@
 
   ExpectInitialSearchResults(client, 4);
   engine->StartFind("world", /*case_sensitive=*/true);
+  ASSERT_TRUE(engine->SelectFindResult(/*forward=*/true));
 
   EXPECT_CALL(client, NotifyNumberOfFindResultsChanged(_, _)).Times(0);
   EXPECT_CALL(client,
@@ -213,9 +212,9 @@
     InSequence sequence;
 
     EXPECT_CALL(client,
-                NotifySelectedFindResultChanged(1, /*final_result=*/true));
+                NotifySelectedFindResultChanged(0, /*final_result=*/true));
     EXPECT_CALL(client,
-                NotifySelectedFindResultChanged(2, /*final_result=*/true));
+                NotifySelectedFindResultChanged(1, /*final_result=*/true));
   }
   ASSERT_TRUE(engine->SelectFindResult(/*forward=*/true));
   ASSERT_TRUE(engine->SelectFindResult(/*forward=*/true));
@@ -236,7 +235,7 @@
     InSequence sequence;
 
     EXPECT_CALL(client,
-                NotifySelectedFindResultChanged(2, /*final_result=*/true));
+                NotifySelectedFindResultChanged(1, /*final_result=*/true));
   }
   ASSERT_TRUE(engine->SelectFindResult(/*forward=*/true));
 }
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 8c3bac2..5625d331 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1999,7 +1999,6 @@
 }
 
 void PDFiumEngine::AddFindResult(const PDFiumRange& result) {
-  bool first_result = find_results_.empty() && !resume_find_index_.has_value();
   // Figure out where to insert the new location, since we could have
   // started searching midway and now we wrapped.
   size_t result_index;
@@ -2015,10 +2014,6 @@
   find_results_.insert(find_results_.begin() + result_index, result);
   UpdateTickMarks();
   client_->NotifyNumberOfFindResultsChanged(find_results_.size(), false);
-  if (first_result) {
-    DCHECK(!current_find_index_);
-    SelectFindResult(/*forward=*/true);
-  }
 }
 
 bool PDFiumEngine::SelectFindResult(bool forward) {
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index 948979a..8621824 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -766,7 +766,7 @@
   const CompositorRenderPassId render_pass_id{3u};
   const gfx::Rect output_rect(45, 22, 120, 13);
   const gfx::Transform transform_to_root =
-      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   const gfx::Rect damage_rect(56, 123, 19, 43);
   cc::FilterOperations filters;
   filters.Append(cc::FilterOperation::CreateBlurFilter(0.f));
@@ -924,7 +924,7 @@
   const gfx::Rect output_rect(45, 22, 120, 13);
   const gfx::Rect damage_rect(56, 123, 19, 43);
   const gfx::Transform transform_to_root =
-      gfx::Transform::AffineForTesting(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
+      gfx::Transform::Affine(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
   const absl::optional<gfx::RRectF> backdrop_filter_bounds;
   SubtreeCaptureId subtree_capture_id;
   const bool has_transparent_background = true;
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index af67599..2704b80 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -2401,8 +2401,8 @@
           ],
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "merge": {
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index e24e81cd..a7d1c0f4 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -1644,8 +1644,8 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "merge": {
@@ -3237,8 +3237,8 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "args": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 0289138..47efdfce 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1674,8 +1674,8 @@
           "hard_timeout": 7200,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "args": [
@@ -93084,8 +93084,8 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 3c3a15e..dade93e 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -5190,8 +5190,8 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "args": [
@@ -7054,8 +7054,8 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "ash_crosapi_browsertests",
-        "test_id_prefix": "ninja://chrome/test:ash_crosapi_browsertests/"
+        "test": "ash_crosapi_tests",
+        "test_id_prefix": "ninja://chrome/test:ash_crosapi_tests/"
       },
       {
         "args": [
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 785532f..40ee7c5 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -180,8 +180,8 @@
     "label": "//ash/components:ash_components_unittests",
     "type": "windowed_test_launcher",
   },
-  "ash_crosapi_browsertests": {
-    "label": "//chrome/test:ash_crosapi_browsertests",
+  "ash_crosapi_tests": {
+    "label": "//chrome/test:ash_crosapi_tests",
     "type": "windowed_test_launcher",
   },
   "ash_webui_unittests": {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 7e5ee49..e5dcb3d 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3835,7 +3835,7 @@
       # Chrome OS only.
       'ash_components_unittests': {},
       # TODO(crbug.com/1351793) Enable on CQ when stable.
-      'ash_crosapi_browsertests': {
+      'ash_crosapi_tests': {
         'ci_only': True,
       },
       'ash_unittests': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 43a3a78..5d48139 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8611,31 +8611,6 @@
             ]
         }
     ],
-    "ProcessHtmlDataImmediately": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "AllChunks",
-                    "params": {
-                        "first": "true",
-                        "rest": "true"
-                    },
-                    "enable_features": [
-                        "ProcessHtmlDataImmediately"
-                    ]
-                }
-            ]
-        }
-    ],
     "ProductivityLauncherStudy": [
         {
             "platforms": [
diff --git a/third_party/blink/DEPS b/third_party/blink/DEPS
index 264f392f..583e2c0 100644
--- a/third_party/blink/DEPS
+++ b/third_party/blink/DEPS
@@ -11,8 +11,8 @@
 ]
 
 specific_include_rules = {
-    ".*_test\.cc": [
-        # Some tests use base::RunLoop.
+    ".*_(unit)?test\.cc": [
         "+base/run_loop.h",
+        "+base/test",
     ],
 }
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 296e129..e6ccc41 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1654,5 +1654,17 @@
         IsolateSandboxedIframesGrouping::kPerSite,
         &isolated_sandboxed_iframes_grouping_types};
 
+BASE_FEATURE(kSSVTrailerWriteNewVersion,
+             "SSVTrailerWriteNewVersion",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+BASE_FEATURE(kSSVTrailerWriteExposureAssertion,
+             "SSVTrailerWriteExposureAssertion",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+BASE_FEATURE(kSSVTrailerEnforceExposureAssertion,
+             "SSVTrailerEnforceExposureAssertion",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/common/messaging/string_message_codec.cc b/third_party/blink/common/messaging/string_message_codec.cc
index 0649fe9..b6f556d3 100644
--- a/third_party/blink/common/messaging/string_message_codec.cc
+++ b/third_party/blink/common/messaging/string_message_codec.cc
@@ -145,16 +145,34 @@
   base::BufferIterator<const uint8_t> iter(message.encoded_message);
   uint8_t tag;
 
-  // Discard any leading version and padding tags.
-  // There may be more than one version, due to Blink and V8 having separate
-  // version tags.
-  do {
+  // Discard the outer envelope, including trailer info if applicable.
+  if (!ReadUint8(iter, &tag))
+    return absl::nullopt;
+  if (tag == kVersionTag) {
+    uint32_t version = 0;
+    if (!ReadUint32(iter, &version))
+      return absl::nullopt;
+    static constexpr uint32_t kMinWireFormatVersionWithTrailer = 21;
+    if (version >= kMinWireFormatVersionWithTrailer) {
+      // In these versions, we expect kTrailerOffsetTag (0xFE) followed by an
+      // offset and size. See details in
+      // third_party/blink/renderer/core/v8/serialization/serialization_tag.h.
+      auto span = iter.Span<uint8_t>(1 + sizeof(uint64_t) + sizeof(uint32_t));
+      if (span.empty() || span[0] != 0xFE)
+        return absl::nullopt;
+    }
     if (!ReadUint8(iter, &tag))
       return absl::nullopt;
+  }
+
+  // Discard any leading version and padding tags.
+  while (tag == kVersionTag || tag == kPaddingTag) {
     uint32_t version;
     if (tag == kVersionTag && !ReadUint32(iter, &version))
       return absl::nullopt;
-  } while (tag == kVersionTag || tag == kPaddingTag);
+    if (!ReadUint8(iter, &tag))
+      return absl::nullopt;
+  }
 
   switch (tag) {
     case kOneByteStringTag: {
diff --git a/third_party/blink/common/messaging/string_message_codec_unittest.cc b/third_party/blink/common/messaging/string_message_codec_unittest.cc
index 9a9b4d9..2bdc67c2 100644
--- a/third_party/blink/common/messaging/string_message_codec_unittest.cc
+++ b/third_party/blink/common/messaging/string_message_codec_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/test/task_environment.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -247,20 +248,52 @@
                 message);
 }
 
+TransferableMessage TransferableMessageFromRawData(std::vector<uint8_t> data) {
+  TransferableMessage message;
+  message.owned_encoded_message = std::move(data);
+  message.encoded_message = message.owned_encoded_message;
+  return message;
+}
+
 TEST(StringMessageCodecTest, Overflow) {
   const std::vector<uint8_t> kOverflowOneByteData{'"', 0xff, 0xff, 0xff, 0x7f};
+  EXPECT_FALSE(DecodeToWebMessagePayload(
+      TransferableMessageFromRawData(kOverflowOneByteData)));
+
   const std::vector<uint8_t> kOverflowTwoByteData{'c', 0xff, 0xff, 0xff, 0x7f};
+  EXPECT_FALSE(DecodeToWebMessagePayload(
+      TransferableMessageFromRawData(kOverflowTwoByteData)));
+}
 
-  TransferableMessage one_byte_message;
-  one_byte_message.owned_encoded_message = kOverflowOneByteData;
-  one_byte_message.encoded_message = one_byte_message.owned_encoded_message;
+TEST(StringMessageCodecTest, InvalidDecode) {
+  auto decode_from_raw = [](std::vector<uint8_t> data) {
+    return DecodeToWebMessagePayload(
+        TransferableMessageFromRawData(std::move(data)));
+  };
 
-  TransferableMessage two_byte_message;
-  two_byte_message.owned_encoded_message = kOverflowTwoByteData;
-  two_byte_message.encoded_message = two_byte_message.owned_encoded_message;
+  EXPECT_FALSE(decode_from_raw({})) << "no data";
+  EXPECT_FALSE(decode_from_raw({0xff, 0x01})) << "only one version";
+  EXPECT_FALSE(decode_from_raw({0xff, 0x80}))
+      << "end of buffer during first version";
+  EXPECT_FALSE(decode_from_raw({0xff, 0x01, 0xff, 0x01}))
+      << "only two versions";
+  EXPECT_FALSE(decode_from_raw({0xff, 0x10, 0xff, 0x80}))
+      << "end of buffer during second version";
+  EXPECT_FALSE(decode_from_raw({0xff, 0x15, 0xfe, 0xff, 0x01, '"', 0x01, 'a'}))
+      << "end of buffer during trailer offset";
+  EXPECT_FALSE(decode_from_raw({0xff, 0x15, 0x7f, 0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                0x00, 0xff, 0x10, '"',  0x01, 'a'}))
+      << "unrecognized trailer offset tag";
 
-  EXPECT_FALSE(DecodeToWebMessagePayload(one_byte_message));
-  EXPECT_FALSE(DecodeToWebMessagePayload(two_byte_message));
+  // Confirm that aside from the specific errors above, this encoding is
+  // generally correct.
+  auto valid_payload = decode_from_raw(
+      {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x10, '"',  0x01, 'a'});
+  ASSERT_TRUE(valid_payload.has_value());
+  ASSERT_TRUE(absl::holds_alternative<std::u16string>(*valid_payload));
+  EXPECT_EQ(absl::get<std::u16string>(*valid_payload), u"a");
 }
 
 }  // namespace
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index fb89eed..fafe80c 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -717,7 +717,7 @@
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kInvalidationSetClassBloomFilter);
 
 // Whether the pending beacon API is enabled or not.
-// https://github.com/WICG/unload-beacon/blob/main/README.md
+// https://github.com/WICG/pending-beacon/blob/main/README.md
 // - kPendingBeaconAPI = {true: {"requires_origin_trial": false}} to enable the
 //   features globally.
 // - kPendingBeaconAPI = {true: {"requires_origin_trial": true}} to enable the
@@ -731,7 +731,7 @@
     kPendingBeaconAPIRequiresOriginTrial;
 // Allows control to decide whether to forced sending out beacons on navigating
 // away a page (transitioning to dispatch pagehide event).
-// Details in https://github.com/WICG/unload-beacon/issues/30
+// Details in https://github.com/WICG/pending-beacon/issues/30
 BLINK_COMMON_EXPORT extern const base::FeatureParam<bool>
     kPendingBeaconAPIForcesSendingOnNavigation;
 
@@ -890,6 +890,29 @@
 // kIsolateSandboxedIframes and returns true if either is enabled.
 BLINK_COMMON_EXPORT bool IsNewBaseUrlInheritanceBehaviorEnabled();
 
+// These control a major serialization change to include information about
+// exposed interfaces in trailer data, to allow emergency fixes.
+// Regardless, data which might have been serialized to disk must continue to be
+// deserializable. These should be removed after a couple milestones.
+//
+// See https://crbug.com/1341844.
+//
+// `kSSVTrailerWriteNewVersion`
+//   If disabled, Blink will revert to writing a pre-trailer format.
+//   This will become impractical once any other incompatible wire format
+//   changes are made.
+// `kSSVTrailerWriteExposureAssertion`
+//   If enabled, Blink will include assertions about which interfaces are
+//   exposed in trailers to serialized messages. Has no effect if
+//   kSSVTrailerWriteNewVersion is disabled.
+// `kSSVTrailerEnforceExposureAssertion`
+//   If enabled, Blink will reject messages which cannot be deserialized in the
+//   current realm. Otherwise, all interfaces will be treated as exposed in all
+//   contexts for the purposes of serialization.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kSSVTrailerWriteNewVersion);
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kSSVTrailerWriteExposureAssertion);
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kSSVTrailerEnforceExposureAssertion);
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/frame/pending_beacon.mojom b/third_party/blink/public/mojom/frame/pending_beacon.mojom
index eca82f8a..b171aff 100644
--- a/third_party/blink/public/mojom/frame/pending_beacon.mojom
+++ b/third_party/blink/public/mojom/frame/pending_beacon.mojom
@@ -21,7 +21,7 @@
 // All methods are called by renderer.
 //
 // API explainer here:
-// https://github.com/WICG/unload-beacon/blob/main/README.md
+// https://github.com/WICG/pending-beacon/blob/main/README.md
 interface PendingBeaconHost {
 
   // Creates a new pending beacon object in the browser side.
diff --git a/third_party/blink/public/web/web_serialized_script_value_version.h b/third_party/blink/public/web/web_serialized_script_value_version.h
index faea82b..19e2663a 100644
--- a/third_party/blink/public/web/web_serialized_script_value_version.h
+++ b/third_party/blink/public/web/web_serialized_script_value_version.h
@@ -38,7 +38,7 @@
 // Embedders may serialize this as out-of-band metadata along with
 // collections of serialized data so that version skew can be detected
 // before deserializing individual values.
-const unsigned kSerializedScriptValueVersion = 20;
+const unsigned kSerializedScriptValueVersion = 21;
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/DEPS b/third_party/blink/renderer/DEPS
index f9604720..24664b7f 100644
--- a/third_party/blink/renderer/DEPS
+++ b/third_party/blink/renderer/DEPS
@@ -9,6 +9,7 @@
     "+base/check_op.h",
     "+base/compiler_specific.h",
     "+base/containers/adapters.h",
+    "+base/containers/buffer_iterator.h",
     "+base/containers/contains.h",
     "+base/containers/cxx20_erase.h",
     "+base/containers/enum_set.h",
@@ -67,6 +68,7 @@
     "+base/containers/lru_cache.h",
     "+base/token.h",
     "+base/trace_event",
+    "+base/types/expected.h",
     "+base/types/optional_util.h",
     "+base/types/pass_key.h",
     "+base/types/strong_alias.h",
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index b7803765b..a6fe052 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -115,6 +115,10 @@
                     "core/v8/serialization/serialized_script_value.h",
                     "core/v8/serialization/serialized_script_value_factory.cc",
                     "core/v8/serialization/serialized_script_value_factory.h",
+                    "core/v8/serialization/trailer_reader.cc",
+                    "core/v8/serialization/trailer_reader.h",
+                    "core/v8/serialization/trailer_writer.cc",
+                    "core/v8/serialization/trailer_writer.h",
                     "core/v8/serialization/transferables.cc",
                     "core/v8/serialization/transferables.h",
                     "core/v8/serialization/unpacked_serialized_script_value.cc",
@@ -231,6 +235,8 @@
           "core/v8/v8_script_runner_test.cc",
           "core/v8/serialization/serialized_script_value_test.cc",
           "core/v8/serialization/serialized_script_value_threaded_test.cc",
+          "core/v8/serialization/trailer_reader_test.cc",
+          "core/v8/serialization/trailer_writer_test.cc",
           "core/v8/serialization/v8_script_value_serializer_test.cc",
           "core/v8/script_promise_tester.cc",
           "core/v8/script_promise_tester.h",
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
index d15e2c7..ed1528fba 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
@@ -128,7 +128,13 @@
   kDeprecatedDetectedTextTag = 't',
 
   kDOMExceptionTag = 'x',  // name:String,message:String,stack:String
-  kVersionTag = 0xFF       // version:uint32_t -> Uses this as the file version.
+  kTrailerOffsetTag =
+      0xFE,  // offset:uint64_t (fixed width, network order) from buffer start
+             // size:uint32_t (fixed width, network order)
+  kVersionTag = 0xFF,  // version:uint32_t -> Uses this as the file version.
+
+  // Tags used in trailers.
+  kTrailerRequiresInterfacesTag = 0xA0,
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc
index 21e4a3f..cad3720 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/transferables.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -59,6 +60,7 @@
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -66,6 +68,16 @@
 
 namespace blink {
 
+namespace {
+
+SerializedScriptValue::CanDeserializeInCallback& GetCanDeserializeInCallback() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      SerializedScriptValue::CanDeserializeInCallback, g_callback, ());
+  return g_callback;
+}
+
+}  // namespace
+
 scoped_refptr<SerializedScriptValue> SerializedScriptValue::Serialize(
     v8::Isolate* isolate,
     v8::Local<v8::Value> value,
@@ -672,6 +684,31 @@
   return file_system_access_tokens_.size() > 0 || wasm_modules_.size() > 0;
 }
 
+bool SerializedScriptValue::CanDeserializeIn(
+    ExecutionContext* execution_context) {
+  TrailerReader reader(GetWireData());
+  if (auto result = reader.SkipToTrailer(); !result.has_value())
+    return false;
+  if (auto result = reader.Read(); !result.has_value())
+    return false;
+  auto& factory = SerializedScriptValueFactory::Instance();
+  bool result = base::ranges::all_of(
+      reader.required_exposed_interfaces(), [&](SerializationTag tag) {
+        return factory.ExecutionContextExposesInterface(execution_context, tag);
+      });
+  if (const auto& callback = GetCanDeserializeInCallback())
+    result = callback.Run(*this, execution_context, result);
+  return result;
+}
+
+// static
+void SerializedScriptValue::OverrideCanDeserializeInForTesting(
+    SerializedScriptValue::CanDeserializeInCallback callback) {
+  auto& global = GetCanDeserializeInCallback();
+  CHECK_NE(callback.is_null(), global.is_null());
+  global = std::move(callback);
+}
+
 // This ensures that the version number published in
 // WebSerializedScriptValueVersion.h matches the serializer's understanding.
 // TODO(jbroman): Fix this to also account for the V8-side version. See
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
index 564b344..f8bcc2c 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
@@ -33,6 +33,7 @@
 
 #include <memory>
 
+#include "base/callback_forward.h"
 #include "base/containers/span.h"
 #include "base/dcheck_is_on.h"
 #include "base/ranges/algorithm.h"
@@ -124,6 +125,8 @@
   //             support color space information, compression, etc.
   // Version 19: Add DetectedBarcode, DetectedFace, and DetectedText support.
   // Version 20: Remove DetectedBarcode, DetectedFace, and DetectedText support.
+  // Version 21: Add support for trailer data which marks required exposed
+  //             interfaces.
   //
   // The following versions cannot be used, in order to be able to
   // deserialize version 0 SSVs. The class implementation has details.
@@ -136,7 +139,11 @@
   //
   // Recent changes are routinely reverted in preparation for branch, and this
   // has been the cause of at least one bug in the past.
-  static constexpr uint32_t kWireFormatVersion = 20;
+  //
+  // WARNING: if you're changing this from version 21, your change will interact
+  // with the fix to https://crbug.com/1341844. Consult bug owner before
+  // proceeding. (After that is settled, remove this paragraph.)
+  static constexpr uint32_t kWireFormatVersion = 21;
 
   // This enumeration specifies whether we're serializing a value for storage;
   // e.g. when writing to IndexedDB. This corresponds to the forStorage flag of
@@ -299,6 +306,29 @@
   // access.
   bool IsOriginCheckRequired() const;
 
+  // Returns true if it is expected to be possible to deserialize this value in
+  // the provided context. It might not be if, for instance, the value contains
+  // interfaces not exposed in all realms.
+  bool CanDeserializeIn(ExecutionContext*);
+
+  // Testing hook to allow overriding whether a value can be deserialized in a
+  // particular execution context. Callers are responsible for assuring thread
+  // safety, and resetting this after the test.
+  using CanDeserializeInCallback =
+      base::RepeatingCallback<bool(const SerializedScriptValue&,
+                                   ExecutionContext*,
+                                   bool can_deserialize)>;
+  static void OverrideCanDeserializeInForTesting(CanDeserializeInCallback);
+  struct ScopedOverrideCanDeserializeInForTesting {
+    explicit ScopedOverrideCanDeserializeInForTesting(
+        CanDeserializeInCallback callback) {
+      OverrideCanDeserializeInForTesting(std::move(callback));
+    }
+    ~ScopedOverrideCanDeserializeInForTesting() {
+      OverrideCanDeserializeInForTesting({});
+    }
+  };
+
   // Derive from Attachments to define collections of objects to serialize in
   // modules. They can be registered using GetOrCreateAttachment().
   class Attachment {
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.cc b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.cc
index 03c72f6e..15fa270 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.cc
@@ -52,4 +52,11 @@
   return deserializer.Deserialize();
 }
 
+bool SerializedScriptValueFactory::ExecutionContextExposesInterface(
+    ExecutionContext* execution_context,
+    SerializationTag interface_tag) {
+  return V8ScriptValueDeserializer::ExecutionContextExposesInterface(
+      execution_context, interface_tag);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h
index 85b12ab..4705030 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_SERIALIZED_SCRIPT_VALUE_FACTORY_H_
 
 #include "base/notreached.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -55,6 +56,9 @@
       v8::Isolate*,
       const SerializedScriptValue::DeserializeOptions&);
 
+  virtual bool ExecutionContextExposesInterface(ExecutionContext*,
+                                                SerializationTag);
+
   // Following methods are expected to be called in
   // SerializedScriptValueFactory{ForModules}.
   SerializedScriptValueFactory() = default;
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.cc b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.cc
new file mode 100644
index 0000000..f69286e
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.cc
@@ -0,0 +1,119 @@
+// 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/bindings/core/v8/serialization/trailer_reader.h"
+
+#include "base/numerics/clamped_math.h"
+#include "base/sys_byteorder.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
+
+namespace blink {
+
+TrailerReader::TrailerReader(base::span<const uint8_t> span)
+    : iterator_(span) {}
+
+TrailerReader::~TrailerReader() = default;
+
+base::expected<bool, TrailerReader::Error> TrailerReader::SkipToTrailer() {
+  DCHECK_EQ(iterator_.position(), 0u);
+
+  auto invalid_header = [this]() {
+    iterator_.TruncateTo(0);
+    return base::unexpected(Error::kInvalidHeader);
+  };
+  auto no_trailer = [this]() {
+    iterator_.TruncateTo(0);
+    return false;
+  };
+
+  // We expect to see a version tag. If we see one, proceed.
+  // If we don't, maybe it's an old serialized value. If we see nothing at all,
+  // this message is apparently blank?
+  const uint8_t* byte = iterator_.Object<uint8_t>();
+  if (!byte)
+    return invalid_header();
+  if (*byte != kVersionTag)
+    return no_trailer();
+
+  // Read the version as a varint. If it overflows or doesn't terminate, that's
+  // a problem.
+  uint32_t version = 0;
+  unsigned version_shift = 0;
+  do {
+    byte = iterator_.Object<uint8_t>();
+    if (!byte || version_shift >= sizeof(version) * 8)
+      return invalid_header();
+    version |= (*byte & 0x7F) << version_shift;
+    version_shift += 7;
+  } while (*byte & 0x80);
+
+  // Validate the version number.
+  if (version < kMinWireFormatVersion)
+    return no_trailer();
+  if (version > SerializedScriptValue::kWireFormatVersion)
+    return invalid_header();
+
+  // We expect to see a tag indicating the trailer offset.
+  byte = iterator_.Object<uint8_t>();
+  if (!byte || *byte != kTrailerOffsetTag)
+    return invalid_header();
+
+  // Here and below, note that we cannot simply call BufferIterator::Object for
+  // uint64_t, since that would require proper alignment to avoid undefined
+  // behavior.
+  uint64_t trailer_offset = 0;
+  if (auto offset_raw = iterator_.CopyObject<uint64_t>())
+    trailer_offset = base::NetToHost64(*offset_raw);
+  else
+    return invalid_header();
+
+  uint32_t trailer_size = 0;
+  if (auto size_raw = iterator_.CopyObject<uint32_t>())
+    trailer_size = base::NetToHost32(*size_raw);
+  else
+    return invalid_header();
+
+  // If there's no trailer, we're done here.
+  if (trailer_size == 0 && trailer_offset == 0)
+    return no_trailer();
+
+  // Otherwise, validate that its offset and size are sensible.
+  if (trailer_offset < iterator_.position() ||
+      base::ClampAdd(trailer_offset, trailer_size) > iterator_.total_size()) {
+    return invalid_header();
+  }
+
+  iterator_.Seek(static_cast<size_t>(trailer_offset));
+  iterator_.TruncateTo(trailer_size);
+  return true;
+}
+
+base::expected<void, TrailerReader::Error> TrailerReader::Read() {
+  while (const uint8_t* tag = iterator_.Object<uint8_t>()) {
+    if (*tag != kTrailerRequiresInterfacesTag)
+      return base::unexpected(Error::kInvalidTrailer);
+    if (required_exposed_interfaces_.size())
+      return base::unexpected(Error::kInvalidTrailer);
+
+    uint32_t num_exposed = 0;
+    if (auto num_exposed_raw = iterator_.CopyObject<uint32_t>())
+      num_exposed = base::NetToHost32(*num_exposed_raw);
+    else
+      return base::unexpected(Error::kInvalidTrailer);
+
+    auto exposed_raw = iterator_.Span<uint8_t>(num_exposed);
+    if (exposed_raw.size() != num_exposed)
+      return base::unexpected(Error::kInvalidTrailer);
+
+    required_exposed_interfaces_.Grow(num_exposed);
+    std::transform(exposed_raw.begin(), exposed_raw.end(),
+                   required_exposed_interfaces_.begin(), [](uint8_t raw) {
+                     return static_cast<SerializationTag>(raw);
+                   });
+  }
+  return {};
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.h b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.h
new file mode 100644
index 0000000..ed79db5
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.h
@@ -0,0 +1,52 @@
+// 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_BINDINGS_CORE_V8_SERIALIZATION_TRAILER_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_TRAILER_READER_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+
+#include "base/containers/buffer_iterator.h"
+#include "base/types/expected.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class CORE_EXPORT TrailerReader {
+ public:
+  enum class Error { kInvalidHeader, kInvalidTrailer };
+  static constexpr uint32_t kMinWireFormatVersion = 21;
+
+  explicit TrailerReader(base::span<const uint8_t>);
+  ~TrailerReader();
+
+  // Call before |Read| if this was initialized with a full message, rather than
+  // just the trailer. Returns whether a trailer was found, or an error.
+  // If no trailer is found, it is still safe to call |Read|.
+  // If an error is returned, this object is left in an unspecified state.
+  [[nodiscard]] base::expected<bool, Error> SkipToTrailer();
+
+  // Read trailer data from the current iterator position, and populates the
+  // below data. If an error is returned, this object is left in an unspecified
+  // state.
+  [[nodiscard]] base::expected<void, Error> Read();
+
+  // List of serialization tags, in any order, corresponding to WebIDL
+  // interfaces which must be exposed in the receiving realm.
+  // In practice these should be unique, but this is not validated.
+  base::span<const SerializationTag> required_exposed_interfaces() const {
+    return required_exposed_interfaces_;
+  }
+
+  size_t GetPositionForTesting() const { return iterator_.position(); }
+
+ private:
+  base::BufferIterator<const uint8_t> iterator_;
+  Vector<SerializationTag> required_exposed_interfaces_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_TRAILER_READER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader_test.cc
new file mode 100644
index 0000000..d4a282b
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader_test.cc
@@ -0,0 +1,191 @@
+// 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/bindings/core/v8/serialization/trailer_reader.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+
+using ::testing::UnorderedElementsAre;
+
+namespace blink {
+namespace {
+
+MATCHER(FoundTrailer, "") {
+  return arg.has_value() && arg.value();
+}
+MATCHER(FoundNoTrailer, "") {
+  return arg.has_value() && !arg.value();
+}
+MATCHER(SawInvalidHeader, "") {
+  return !arg.has_value() &&
+         arg.error() == TrailerReader::Error::kInvalidHeader;
+}
+
+MATCHER(Succeeded, "") {
+  return arg.has_value();
+}
+MATCHER(SawInvalidTrailer, "") {
+  return !arg.has_value() &&
+         arg.error() == TrailerReader::Error::kInvalidTrailer;
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_Empty) {
+  TrailerReader reader({});
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_NoVersion) {
+  constexpr uint8_t kData[] = {'0'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), FoundNoTrailer());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_VersionTooLow) {
+  constexpr uint8_t kData[] = {0xff, 0x09, '0'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), FoundNoTrailer());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_VersionTooHigh) {
+  constexpr uint8_t kData[] = {0xff, 0xff, 0xff, 0x00};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_VersionOverflow) {
+  constexpr uint8_t kData[] = {0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_NoTrailerTag) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xff, 0x0f, '0'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_TruncatedOffset) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_TruncatedSize) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_NoTrailer) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0xff, 0x0f, '0'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), FoundNoTrailer());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_OffsetTooSmall) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x01, 0xff, 0x0f, '0'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_OffsetTooLarge) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+                               0x00, 0x00, 0x10, 0xff, 0x0f, '0'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_SizeTooLarge) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0xff,
+                               0x0f, '0',  't',  'e',  's',  't'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), SawInvalidHeader());
+}
+
+TEST(TrailerReaderTest, SkipToTrailer_ValidRange) {
+  constexpr uint8_t kData[] = {0xff, 0x15, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x04, 0xff,
+                               0x0f, '0',  't',  'e',  's',  't'};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.SkipToTrailer(), FoundTrailer());
+  EXPECT_EQ(reader.GetPositionForTesting(), 18u);
+}
+
+TEST(TrailerReaderTest, Read_Empty) {
+  TrailerReader reader({});
+  EXPECT_THAT(reader.Read(), Succeeded());
+  EXPECT_THAT(reader.required_exposed_interfaces(), ::testing::IsEmpty());
+}
+
+TEST(TrailerReaderTest, Read_UnrecognizedTrailerTag) {
+  constexpr uint8_t kData[] = {0x32, 0x00, 0x00, 0x00, 0x00};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.Read(), SawInvalidTrailer());
+}
+
+TEST(TrailerReaderTest, Read_TruncatedInterfaceCount) {
+  constexpr uint8_t kData[] = {0x32, 0x00, 0x00, 0x00};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.Read(), SawInvalidTrailer());
+}
+
+TEST(TrailerReaderTest, Read_TruncatedExposedInterfaces) {
+  constexpr uint8_t kData[] = {0xa0, 0x00, 0x00, 0x00, 0x02, kImageBitmapTag};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.Read(), SawInvalidTrailer());
+}
+
+TEST(TrailerReaderTest, Read_ZeroInterfaceCount) {
+  constexpr uint8_t kData[] = {0xa0, 0x00, 0x00, 0x00, 0x00};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.Read(), Succeeded());
+  EXPECT_THAT(reader.required_exposed_interfaces(), ::testing::IsEmpty());
+}
+
+TEST(TrailerReaderTest, Read_ValidExposedInterfaces) {
+  constexpr uint8_t kData[] = {
+      0xa0, 0x00, 0x00, 0x00, 0x02, kImageBitmapTag, kCryptoKeyTag};
+  TrailerReader reader(kData);
+  EXPECT_THAT(reader.Read(), Succeeded());
+  EXPECT_THAT(reader.required_exposed_interfaces(),
+              UnorderedElementsAre(kImageBitmapTag, kCryptoKeyTag));
+}
+
+TEST(TrailerReaderTest, Read_AfterSkipToTrailer) {
+  constexpr uint8_t kData[] = {
+      0xff,         0x15, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00,         0x00, 0x12, 0x00, 0x00, 0x00, 0x07, 0xff,
+      0x0f,         '0',  0xa0, 0x00, 0x00, 0x00, 0x02, kImageBitmapTag,
+      kCryptoKeyTag};
+  TrailerReader reader(kData);
+  ASSERT_THAT(reader.SkipToTrailer(), FoundTrailer());
+  EXPECT_EQ(reader.GetPositionForTesting(), 18u);
+  ASSERT_THAT(reader.Read(), Succeeded());
+  EXPECT_THAT(reader.required_exposed_interfaces(),
+              UnorderedElementsAre(kImageBitmapTag, kCryptoKeyTag));
+}
+
+TEST(TrailerReaderTest, Read_AfterSkipToTrailer_SizeTooSmall) {
+  constexpr uint8_t kData[] = {
+      0xff,         0x15, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00,         0x00, 0x12, 0x00, 0x00, 0x00, 0x05, 0xff,
+      0x0f,         '0',  0xa0, 0x00, 0x00, 0x00, 0x02, kImageBitmapTag,
+      kCryptoKeyTag};
+  TrailerReader reader(kData);
+  ASSERT_THAT(reader.SkipToTrailer(), FoundTrailer());
+  EXPECT_EQ(reader.GetPositionForTesting(), 18u);
+  ASSERT_THAT(reader.Read(), SawInvalidTrailer());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.cc
new file mode 100644
index 0000000..32cf1b6
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.cc
@@ -0,0 +1,42 @@
+// 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/bindings/core/v8/serialization/trailer_writer.h"
+
+#include "base/feature_list.h"
+#include "base/sys_byteorder.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+
+namespace blink {
+
+TrailerWriter::TrailerWriter() = default;
+
+TrailerWriter::~TrailerWriter() = default;
+
+void TrailerWriter::RequireExposedInterface(SerializationTag tag) {
+  DCHECK_GT(tag, 0x00);
+  DCHECK_LE(tag, 0xFF);
+  if (!requires_exposed_interfaces_.Contains(tag))
+    requires_exposed_interfaces_.push_back(tag);
+}
+
+Vector<uint8_t> TrailerWriter::MakeTrailerData() const {
+  Vector<uint8_t> trailer;
+  if (wtf_size_t num_exposed = requires_exposed_interfaces_.size();
+      num_exposed && base::FeatureList::IsEnabled(
+                         features::kSSVTrailerWriteExposureAssertion)) {
+    uint32_t num_exposed_enc = base::HostToNet32(num_exposed);
+    wtf_size_t start = trailer.size();
+    trailer.Grow(start + 1 + sizeof(uint32_t) + num_exposed);
+    trailer[start] = kTrailerRequiresInterfacesTag;
+    memcpy(&trailer[start + 1], &num_exposed_enc, sizeof(uint32_t));
+    std::copy(requires_exposed_interfaces_.begin(),
+              requires_exposed_interfaces_.end(),
+              &trailer[start + 1 + sizeof(uint32_t)]);
+  }
+  return trailer;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.h b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.h
new file mode 100644
index 0000000..0fc556c
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.h
@@ -0,0 +1,29 @@
+// 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_BINDINGS_CORE_V8_SERIALIZATION_TRAILER_WRITER_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_TRAILER_WRITER_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class CORE_EXPORT TrailerWriter {
+ public:
+  TrailerWriter();
+  ~TrailerWriter();
+
+  void RequireExposedInterface(SerializationTag);
+
+  Vector<uint8_t> MakeTrailerData() const;
+
+ private:
+  Vector<SerializationTag> requires_exposed_interfaces_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_TRAILER_WRITER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer_test.cc
new file mode 100644
index 0000000..f39382a
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer_test.cc
@@ -0,0 +1,39 @@
+// 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/bindings/core/v8/serialization/trailer_writer.h"
+
+#include "base/containers/span.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+
+using ::testing::ElementsAre;
+using ::testing::UnorderedElementsAre;
+
+namespace blink {
+namespace {
+
+TEST(TrailerWriterTest, Empty) {
+  TrailerWriter writer;
+  EXPECT_THAT(writer.MakeTrailerData(), ElementsAre());
+}
+
+TEST(TrailerWriterTest, ExposedInterfaces) {
+  TrailerWriter writer;
+  writer.RequireExposedInterface(kImageBitmapTag);
+  writer.RequireExposedInterface(kCryptoKeyTag);
+  writer.RequireExposedInterface(kImageBitmapTag);
+
+  // Duplicates should be removed, but we're otherwise indifferent to the order.
+  auto trailer = writer.MakeTrailerData();
+  ASSERT_EQ(trailer.size(), 7u);
+  EXPECT_THAT(base::make_span(trailer).first(5),
+              ElementsAre(0xA0, 0x00, 0x00, 0x00, 0x02));
+  EXPECT_THAT(base::make_span(trailer).subspan(5, 2),
+              UnorderedElementsAre(kImageBitmapTag, kCryptoKeyTag));
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index 3df6e7a..242c444 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -6,13 +6,36 @@
 
 #include <limits>
 
+#include "base/feature_list.h"
 #include "base/numerics/checked_math.h"
 #include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_matrix.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_matrix_read_only.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_point.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_init.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_read_only.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_quad.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_read_only.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_file.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_file_list.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_image_data.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_message_port.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_mojo_handle.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_offscreen_canvas.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_transform_stream.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
@@ -93,6 +116,16 @@
   if (version < kMinVersionForSeparateEnvelope)
     return 0;
 
+  // These versions expect a trailer offset in the envelope.
+  if (version >= TrailerReader::kMinWireFormatVersion) {
+    static constexpr size_t kTrailerOffsetDataSize =
+        1 + sizeof(uint64_t) + sizeof(uint32_t);
+    DCHECK_LT(i, std::numeric_limits<size_t>::max() - kTrailerOffsetDataSize);
+    i += kTrailerOffsetDataSize;
+    if (i >= length)
+      return 0;
+  }
+
   // Otherwise, we did read the envelope. Hurray!
   *out_version = version;
   return i;
@@ -252,6 +285,10 @@
 ScriptWrappable* V8ScriptValueDeserializer::ReadDOMObject(
     SerializationTag tag,
     ExceptionState& exception_state) {
+  if (!ExecutionContextExposesInterface(
+          ExecutionContext::From(GetScriptState()), tag)) {
+    return nullptr;
+  }
   switch (tag) {
     case kBlobTag: {
       if (Version() < 3)
@@ -819,4 +856,83 @@
   return nullptr;
 }
 
+// static
+bool V8ScriptValueDeserializer::ExecutionContextExposesInterface(
+    ExecutionContext* execution_context,
+    SerializationTag interface_tag) {
+  if (!base::FeatureList::IsEnabled(
+          features::kSSVTrailerEnforceExposureAssertion)) {
+    return true;
+  }
+
+  // If you're updating this, consider whether you should also update
+  // V8ScriptValueSerializer to call TrailerWriter::RequireExposedInterface
+  // (generally via WriteAndRequireInterfaceTag). Any interface which might
+  // potentially not be exposed on all realms, even if not currently (i.e., most
+  // or all) should probably be listed here.
+  switch (interface_tag) {
+    case kBlobTag:
+    case kBlobIndexTag:
+      return V8Blob::IsExposed(execution_context);
+    case kFileTag:
+    case kFileIndexTag:
+      return V8File::IsExposed(execution_context);
+    case kFileListTag:
+    case kFileListIndexTag: {
+      const bool is_exposed = V8FileList::IsExposed(execution_context);
+      if (is_exposed)
+        DCHECK(V8File::IsExposed(execution_context));
+      return is_exposed;
+    }
+    case kImageBitmapTag:
+    case kImageBitmapTransferTag:
+      return V8ImageBitmap::IsExposed(execution_context);
+    case kImageDataTag:
+      return V8ImageData::IsExposed(execution_context);
+    case kDOMPointTag:
+      return V8DOMPoint::IsExposed(execution_context);
+    case kDOMPointReadOnlyTag:
+      return V8DOMPointReadOnly::IsExposed(execution_context);
+    case kDOMRectTag:
+      return V8DOMRect::IsExposed(execution_context);
+    case kDOMRectReadOnlyTag:
+      return V8DOMRectReadOnly::IsExposed(execution_context);
+    case kDOMQuadTag:
+      return V8DOMQuad::IsExposed(execution_context);
+    case kDOMMatrix2DTag:
+    case kDOMMatrixTag:
+      return V8DOMMatrix::IsExposed(execution_context);
+    case kDOMMatrix2DReadOnlyTag:
+    case kDOMMatrixReadOnlyTag:
+      return V8DOMMatrixReadOnly::IsExposed(execution_context);
+    case kMessagePortTag:
+      return V8MessagePort::IsExposed(execution_context);
+    case kMojoHandleTag:
+      // This would ideally be V8MojoHandle::IsExposed, but WebUSB tests
+      // currently rely on being able to send handles to frames and workers
+      // which don't otherwise have MojoJS exposed.
+      return (execution_context->IsWindow() ||
+              execution_context->IsWorkerGlobalScope()) &&
+             RuntimeEnabledFeatures::MojoJSEnabled();
+    case kOffscreenCanvasTransferTag:
+      return V8OffscreenCanvas::IsExposed(execution_context);
+    case kReadableStreamTransferTag:
+      return V8ReadableStream::IsExposed(execution_context);
+    case kWritableStreamTransferTag:
+      return V8WritableStream::IsExposed(execution_context);
+    case kTransformStreamTransferTag: {
+      const bool is_exposed = V8TransformStream::IsExposed(execution_context);
+      if (is_exposed) {
+        DCHECK(V8ReadableStream::IsExposed(execution_context));
+        DCHECK(V8WritableStream::IsExposed(execution_context));
+      }
+      return is_exposed;
+    }
+    case kDOMExceptionTag:
+      return V8DOMException::IsExposed(execution_context);
+    default:
+      return false;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h
index aaa096d..99cf9b9 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h
@@ -49,6 +49,9 @@
 
   v8::Local<v8::Value> Deserialize();
 
+  static bool ExecutionContextExposesInterface(ExecutionContext*,
+                                               SerializationTag interface_tag);
+
  protected:
   virtual ScriptWrappable* ReadDOMObject(SerializationTag, ExceptionState&);
 
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
index 2197160..480d532 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
@@ -5,9 +5,13 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h"
 
 #include "base/auto_reset.h"
+#include "base/feature_list.h"
+#include "base/sys_byteorder.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
@@ -242,8 +246,25 @@
     return nullptr;
 
   // Write out the file header.
-  WriteTag(kVersionTag);
-  WriteUint32(SerializedScriptValue::kWireFormatVersion);
+  static_assert(
+      SerializedScriptValue::kWireFormatVersion < 0x80,
+      "the following calculation depends on the encoded length of the version");
+  static_assert(SerializedScriptValue::kWireFormatVersion == 21,
+                "The kSSVTrailerWriteNewVersion flag assumes writing version "
+                "20 is otherwise safe.");
+  static constexpr size_t kTrailerOffsetPosition =
+      1 /* version tag */ + 1 /* version */ + 1 /* trailer offset tag */;
+  static constexpr uint8_t kZeroOffset[sizeof(uint64_t) + sizeof(uint32_t)] =
+      {};
+  if (base::FeatureList::IsEnabled(features::kSSVTrailerWriteNewVersion)) {
+    WriteTag(kVersionTag);
+    WriteUint32(SerializedScriptValue::kWireFormatVersion);
+    WriteTag(kTrailerOffsetTag);
+    WriteRawBytes(kZeroOffset, sizeof(kZeroOffset));
+  } else {
+    WriteTag(kVersionTag);
+    WriteUint32(20 /* wire format version before trailers */);
+  }
   serializer_.WriteHeader();
 
   // Serialize the value and handle errors.
@@ -278,8 +299,28 @@
 
   serialized_script_value_->CloneSharedArrayBuffers(shared_array_buffers_);
 
+  // Append the trailer, if applicable.
+  Vector<uint8_t> trailer;
+  if (base::FeatureList::IsEnabled(features::kSSVTrailerWriteNewVersion)) {
+    trailer = trailer_writer_.MakeTrailerData();
+    if (!trailer.empty())
+      WriteRawBytes(trailer.data(), trailer.size());
+  }
+
   // Finalize the results.
   std::pair<uint8_t*, size_t> buffer = serializer_.Release();
+  if (!trailer.empty()) {
+    CHECK(base::FeatureList::IsEnabled(features::kSSVTrailerWriteNewVersion));
+    CHECK_GT(buffer.second, kTrailerOffsetPosition + sizeof(uint64_t) +
+                                sizeof(uint32_t) + trailer.size());
+    const uint64_t trailer_offset =
+        base::HostToNet64(buffer.second - trailer.size());
+    const uint32_t trailer_size = base::HostToNet32(trailer.size());
+    memcpy(buffer.first + kTrailerOffsetPosition, &trailer_offset,
+           sizeof(uint64_t));
+    memcpy(buffer.first + kTrailerOffsetPosition + sizeof(uint64_t),
+           &trailer_size, sizeof(uint32_t));
+  }
   serialized_script_value_->SetData(
       SerializedScriptValue::DataBufferPtr(buffer.first), buffer.second);
   return std::move(serialized_script_value_);
@@ -397,10 +438,10 @@
       DCHECK_LE(index, std::numeric_limits<uint32_t>::max());
       blob_info_array_->emplace_back(blob->GetBlobDataHandle(), blob->type(),
                                      blob->size());
-      WriteTag(kBlobIndexTag);
+      WriteAndRequireInterfaceTag(kBlobIndexTag);
       WriteUint32(static_cast<uint32_t>(index));
     } else {
-      WriteTag(kBlobTag);
+      WriteAndRequireInterfaceTag(kBlobTag);
       WriteUTF8String(blob->Uuid());
       WriteUTF8String(blob->type());
       WriteUint64(blob->size());
@@ -408,14 +449,15 @@
     return true;
   }
   if (auto* file = dispatcher.ToMostDerived<File>()) {
-    WriteTag(blob_info_array_ ? kFileIndexTag : kFileTag);
+    WriteAndRequireInterfaceTag(blob_info_array_ ? kFileIndexTag : kFileTag);
     return WriteFile(file, exception_state);
   }
   if (auto* file_list = dispatcher.ToMostDerived<FileList>()) {
     // This does not presently deduplicate a File object and its entry in a
     // FileList, which is non-standard behavior.
     unsigned length = file_list->length();
-    WriteTag(blob_info_array_ ? kFileListIndexTag : kFileListTag);
+    WriteAndRequireInterfaceTag(blob_info_array_ ? kFileListIndexTag
+                                                 : kFileListTag);
     WriteUint32(length);
     for (unsigned i = 0; i < length; i++) {
       if (!WriteFile(file_list->item(i), exception_state))
@@ -448,7 +490,7 @@
       }
 
       DCHECK_LE(index, std::numeric_limits<uint32_t>::max());
-      WriteTag(kImageBitmapTransferTag);
+      WriteAndRequireInterfaceTag(kImageBitmapTransferTag);
       WriteUint32(static_cast<uint32_t>(index));
       return true;
     }
@@ -463,7 +505,7 @@
           "Non-origin-clean ImageBitmap cannot be cloned.");
       return false;
     }
-    WriteTag(kImageBitmapTag);
+    WriteAndRequireInterfaceTag(kImageBitmapTag);
     SkImageInfo info = image_bitmap->GetBitmapSkImageInfo();
     SerializedImageBitmapSettings bitmap_settings(
         info, image_bitmap->ImageOrientation());
@@ -505,7 +547,7 @@
     return true;
   }
   if (auto* image_data = dispatcher.ToMostDerived<ImageData>()) {
-    WriteTag(kImageDataTag);
+    WriteAndRequireInterfaceTag(kImageDataTag);
     SerializedImageDataSettings settings(
         image_data->GetPredefinedColorSpace(),
         image_data->GetImageDataStorageFormat());
@@ -527,7 +569,7 @@
     return true;
   }
   if (auto* point = dispatcher.ToMostDerived<DOMPoint>()) {
-    WriteTag(kDOMPointTag);
+    WriteAndRequireInterfaceTag(kDOMPointTag);
     WriteDouble(point->x());
     WriteDouble(point->y());
     WriteDouble(point->z());
@@ -535,7 +577,7 @@
     return true;
   }
   if (auto* point = dispatcher.ToMostDerived<DOMPointReadOnly>()) {
-    WriteTag(kDOMPointReadOnlyTag);
+    WriteAndRequireInterfaceTag(kDOMPointReadOnlyTag);
     WriteDouble(point->x());
     WriteDouble(point->y());
     WriteDouble(point->z());
@@ -543,7 +585,7 @@
     return true;
   }
   if (auto* rect = dispatcher.ToMostDerived<DOMRect>()) {
-    WriteTag(kDOMRectTag);
+    WriteAndRequireInterfaceTag(kDOMRectTag);
     WriteDouble(rect->x());
     WriteDouble(rect->y());
     WriteDouble(rect->width());
@@ -551,7 +593,7 @@
     return true;
   }
   if (auto* rect = dispatcher.ToMostDerived<DOMRectReadOnly>()) {
-    WriteTag(kDOMRectReadOnlyTag);
+    WriteAndRequireInterfaceTag(kDOMRectReadOnlyTag);
     WriteDouble(rect->x());
     WriteDouble(rect->y());
     WriteDouble(rect->width());
@@ -559,7 +601,7 @@
     return true;
   }
   if (auto* quad = dispatcher.ToMostDerived<DOMQuad>()) {
-    WriteTag(kDOMQuadTag);
+    WriteAndRequireInterfaceTag(kDOMQuadTag);
     for (const DOMPoint* point :
          {quad->p1(), quad->p2(), quad->p3(), quad->p4()}) {
       WriteDouble(point->x());
@@ -571,7 +613,7 @@
   }
   if (auto* matrix = dispatcher.ToMostDerived<DOMMatrix>()) {
     if (matrix->is2D()) {
-      WriteTag(kDOMMatrix2DTag);
+      WriteAndRequireInterfaceTag(kDOMMatrix2DTag);
       WriteDouble(matrix->a());
       WriteDouble(matrix->b());
       WriteDouble(matrix->c());
@@ -579,7 +621,7 @@
       WriteDouble(matrix->e());
       WriteDouble(matrix->f());
     } else {
-      WriteTag(kDOMMatrixTag);
+      WriteAndRequireInterfaceTag(kDOMMatrixTag);
       WriteDouble(matrix->m11());
       WriteDouble(matrix->m12());
       WriteDouble(matrix->m13());
@@ -601,7 +643,7 @@
   }
   if (auto* matrix = dispatcher.ToMostDerived<DOMMatrixReadOnly>()) {
     if (matrix->is2D()) {
-      WriteTag(kDOMMatrix2DReadOnlyTag);
+      WriteAndRequireInterfaceTag(kDOMMatrix2DReadOnlyTag);
       WriteDouble(matrix->a());
       WriteDouble(matrix->b());
       WriteDouble(matrix->c());
@@ -609,7 +651,7 @@
       WriteDouble(matrix->e());
       WriteDouble(matrix->f());
     } else {
-      WriteTag(kDOMMatrixReadOnlyTag);
+      WriteAndRequireInterfaceTag(kDOMMatrixReadOnlyTag);
       WriteDouble(matrix->m11());
       WriteDouble(matrix->m12());
       WriteDouble(matrix->m13());
@@ -640,7 +682,7 @@
       return false;
     }
     DCHECK_LE(index, std::numeric_limits<uint32_t>::max());
-    WriteTag(kMessagePortTag);
+    WriteAndRequireInterfaceTag(kMessagePortTag);
     WriteUint32(static_cast<uint32_t>(index));
     return true;
   }
@@ -660,7 +702,7 @@
     serialized_script_value_->MojoHandles().push_back(
         mojo_handle->TakeHandle());
     index = serialized_script_value_->MojoHandles().size() - 1;
-    WriteTag(kMojoHandleTag);
+    WriteAndRequireInterfaceTag(kMojoHandleTag);
     WriteUint32(static_cast<uint32_t>(index));
     return true;
   }
@@ -688,7 +730,7 @@
           "because it had a rendering context.");
       return false;
     }
-    WriteTag(kOffscreenCanvasTransferTag);
+    WriteAndRequireInterfaceTag(kOffscreenCanvasTransferTag);
     WriteUint32(canvas->width());
     WriteUint32(canvas->height());
     WriteUint64(canvas->PlaceholderCanvasId());
@@ -715,7 +757,7 @@
           "A ReadableStream could not be cloned because it was locked");
       return false;
     }
-    WriteTag(kReadableStreamTransferTag);
+    WriteAndRequireInterfaceTag(kReadableStreamTransferTag);
     WriteUint32(static_cast<uint32_t>(index));
     return true;
   }
@@ -735,7 +777,7 @@
           "A WritableStream could not be cloned because it was locked");
       return false;
     }
-    WriteTag(kWritableStreamTransferTag);
+    WriteAndRequireInterfaceTag(kWritableStreamTransferTag);
     DCHECK(transferables_);
     // The index calculation depends on the order that TransferReadableStreams
     // and TransferWritableStreams are called in
@@ -765,7 +807,7 @@
           "A TransformStream could not be cloned because it was locked");
       return false;
     }
-    WriteTag(kTransformStreamTransferTag);
+    WriteAndRequireInterfaceTag(kTransformStreamTransferTag);
     DCHECK(transferables_);
     // TransformStreams use two ports each. The stored index is the index of the
     // first one. The first TransformStream is stored in the array after all the
@@ -776,7 +818,7 @@
     return true;
   }
   if (auto* exception = dispatcher.ToMostDerived<DOMException>()) {
-    WriteTag(kDOMExceptionTag);
+    WriteAndRequireInterfaceTag(kDOMExceptionTag);
     WriteUTF8String(exception->name());
     WriteUTF8String(exception->message());
     // We may serialize the stack property in the future, so we store a null
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h
index dad271a..fb56366a 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/trailer_writer.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -73,6 +74,11 @@
   void WriteUnguessableToken(const base::UnguessableToken& token);
   void WriteUTF8String(const String&);
 
+  void WriteAndRequireInterfaceTag(SerializationTag tag) {
+    GetTrailerWriter().RequireExposedInterface(tag);
+    WriteTag(tag);
+  }
+
   template <typename E>
   void WriteUint32Enum(E value) {
     static_assert(
@@ -91,6 +97,8 @@
 
   const Transferables* GetTransferables() const { return transferables_; }
 
+  TrailerWriter& GetTrailerWriter() { return trailer_writer_; }
+
  private:
   // Transfer is split into two phases: scanning the transferables so that we
   // don't have to serialize the data (just an index), and finalizing (to
@@ -128,6 +136,7 @@
   ScriptState* script_state_;
   scoped_refptr<SerializedScriptValue> serialized_script_value_;
   v8::ValueSerializer serializer_;
+  TrailerWriter trailer_writer_;
   const Transferables* transferables_ = nullptr;
   const ExceptionState* exception_state_ = nullptr;
   WebBlobInfoArray* blob_info_array_ = nullptr;
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index 7307bfe..a388957 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -4,12 +4,15 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/trailer_reader.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
@@ -35,6 +38,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_string_resource.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_float32array_uint16array_uint8clampedarray.h"
+#include "third_party/blink/renderer/core/context_features/context_feature_settings.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
 #include "third_party/blink/renderer/core/fileapi/file.h"
@@ -61,6 +65,7 @@
 #include "third_party/blink/renderer/platform/file_metadata.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImage.h"
@@ -1004,6 +1009,10 @@
 
 TEST(V8ScriptValueSerializerTest, RoundTripMojoHandle) {
   V8TestingScope scope;
+  ContextFeatureSettings::From(
+      scope.GetExecutionContext(),
+      ContextFeatureSettings::CreationMode::kCreateIfNotExists)
+      ->EnableMojoJS(true);
 
   mojo::MessagePipe pipe;
   auto* handle = MakeGarbageCollected<MojoHandle>(
@@ -2060,7 +2069,7 @@
 TEST(V8ScriptValueSerializerTest, DecodeWithInefficientVersionEnvelope) {
   V8TestingScope scope;
   scoped_refptr<SerializedScriptValue> input =
-      SerializedValue({0xff, 0x80, 0x09, 0xff, 0x09, 0x54});
+      SerializedValue({0xff, 0x90, 0x00, 0xff, 0x09, 0x54});
   EXPECT_TRUE(
       V8ScriptValueDeserializer(scope.GetScriptState(), std::move(input))
           .Deserialize()
@@ -2180,4 +2189,87 @@
   EXPECT_TRUE(result->IsNull());
 }
 
+TEST(V8ScriptValueSerializerTest, CanDeserializeIn_OldValues) {
+  // This is `true` serialized in version 9. It should still return true from
+  // CanDeserializeIn.
+  V8TestingScope scope;
+  scoped_refptr<SerializedScriptValue> input =
+      SerializedValue({0xff, 0x09, 'T', 0x00});
+  EXPECT_TRUE(input->CanDeserializeIn(scope.GetExecutionContext()));
+}
+
+// TODO(crbug.com/1341844): Remove this along with the rest of the plumbing for
+// `features::kSSVTrailerWriteNewVersion.
+TEST(V8ScriptValueSerializerTest, SSVTrailerWriteNewVersionDisabled) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitAndDisableFeature(features::kSSVTrailerWriteNewVersion);
+  V8TestingScope scope;
+  v8::Isolate* isolate = scope.GetIsolate();
+
+  // Should still be able to write and read the old version.
+  EXPECT_TRUE(RoundTrip(v8::True(isolate), scope)->IsTrue());
+
+  // Should actually be the old version (decimal 20).
+  V8ScriptValueSerializer serializer(scope.GetScriptState());
+  auto value = serializer.Serialize(v8::True(isolate), ASSERT_NO_EXCEPTION);
+  EXPECT_THAT(value->GetWireData().first(2), ::testing::ElementsAre(0xff, 20));
+}
+
+// TODO(crbug.com/1341844): Remove this along with the rest of the plumbing for
+// `features::kSSVTrailerWriteExposureAssertion`.
+TEST(V8ScriptValueSerializerTest, SSVTrailerWriteExposureAssertionDisabled) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitAndDisableFeature(
+      features::kSSVTrailerWriteExposureAssertion);
+  V8TestingScope scope;
+
+  // Even though this has an exposure restriction, it should not be reflected
+  // while the feature to write the assertion is disabled.
+  DOMPoint* point = DOMPoint::Create(0, 0);
+  v8::Local<v8::Value> wrapper =
+      ToV8Traits<DOMPoint>::ToV8(scope.GetScriptState(), point)
+          .ToLocalChecked();
+  V8ScriptValueSerializer serializer(scope.GetScriptState());
+  auto value = serializer.Serialize(wrapper, ASSERT_NO_EXCEPTION);
+  TrailerReader trailer_reader(value->GetWireData());
+  ASSERT_TRUE(trailer_reader.SkipToTrailer().has_value());
+  ASSERT_TRUE(trailer_reader.Read().has_value());
+  EXPECT_THAT(trailer_reader.required_exposed_interfaces(),
+              ::testing::IsEmpty());
+}
+
+// TODO(crbug.com/1341844): Remove this along with the rest of the plumbing for
+// `features::kSSVTrailerWriteExposureAssertion`.
+TEST(V8ScriptValueSerializerTest, SSVTrailerEnforceExposureAssertionDisabled) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitAndDisableFeature(
+      features::kSSVTrailerEnforceExposureAssertion);
+  scoped_refptr<SerializedScriptValue> ssv;
+  mojo::MessagePipe pipe;
+
+  {
+    V8TestingScope scope;
+    ContextFeatureSettings::From(
+        scope.GetExecutionContext(),
+        ContextFeatureSettings::CreationMode::kCreateIfNotExists)
+        ->EnableMojoJS(true);
+    auto* handle = MakeGarbageCollected<MojoHandle>(
+        mojo::ScopedHandle::From(std::move(pipe.handle0)));
+    Transferables transferables;
+    transferables.mojo_handles.push_back(handle);
+
+    V8ScriptValueSerializer::Options options;
+    options.transferables = &transferables;
+    V8ScriptValueSerializer serializer(scope.GetScriptState(), options);
+    ssv = serializer.Serialize(ToV8(handle, scope.GetScriptState()),
+                               ASSERT_NO_EXCEPTION);
+  }
+
+  {
+    ScopedMojoJSForTest disable_mojo_js(false);
+    V8TestingScope scope;
+    EXPECT_TRUE(ssv->CanDeserializeIn(scope.GetExecutionContext()));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.cc
index 26ab160..2c63d161 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.cc
@@ -52,4 +52,11 @@
   return deserializer.Deserialize();
 }
 
+bool SerializedScriptValueForModulesFactory::ExecutionContextExposesInterface(
+    ExecutionContext* execution_context,
+    SerializationTag interface_tag) {
+  return V8ScriptValueDeserializerForModules::ExecutionContextExposesInterface(
+      execution_context, interface_tag);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.h b/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.h
index 5579688..0e9d2ec 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.h
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/serialized_script_value_for_modules_factory.h
@@ -43,6 +43,9 @@
       UnpackedSerializedScriptValue*,
       v8::Isolate*,
       const SerializedScriptValue::DeserializeOptions&) override;
+
+  bool ExecutionContextExposesInterface(ExecutionContext*,
+                                        SerializationTag) override;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
index fd4e4fa..bd8e764 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -4,15 +4,32 @@
 
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h"
 
+#include "base/feature_list.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-blink.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_transfer_token.mojom-blink.h"
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_crypto.h"
 #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h"
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/serialized_track_params.h"
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_crop_target.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_crypto_key.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_file_system_directory_handle.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_file_system_file_handle.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_source_handle.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_certificate.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_audio_frame.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_video_frame.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/modules/crypto/crypto_key.h"
@@ -49,6 +66,10 @@
           V8ScriptValueDeserializer::ReadDOMObject(tag, exception_state))
     return wrappable;
 
+  if (!ExecutionContextExposesInterface(
+          ExecutionContext::From(GetScriptState()), tag)) {
+    return nullptr;
+  }
   switch (tag) {
     case kCryptoKeyTag:
       return ReadCryptoKey();
@@ -637,4 +658,60 @@
       std::move(handle_internals.internal_blob_url));
 }
 
+// static
+bool V8ScriptValueDeserializerForModules::ExecutionContextExposesInterface(
+    ExecutionContext* execution_context,
+    SerializationTag interface_tag) {
+  // If you're updating this, consider whether you should also update
+  // V8ScriptValueSerializerForModules to call
+  // TrailerWriter::RequireExposedInterface (generally via
+  // WriteAndRequireInterfaceTag). Any interface which might potentially not be
+  // exposed on all realms, even if not currently (i.e., most or all) should
+  // probably be listed here.
+  if (V8ScriptValueDeserializer::ExecutionContextExposesInterface(
+          execution_context, interface_tag)) {
+    return true;
+  }
+  DCHECK(base::FeatureList::IsEnabled(
+      features::kSSVTrailerEnforceExposureAssertion))
+      << "V8ScriptValueDeserializer should already have handled this";
+  switch (interface_tag) {
+    case kCryptoKeyTag:
+      return V8CryptoKey::IsExposed(execution_context);
+    case kDOMFileSystemTag:
+      // TODO(crbug.com/1366065): In theory this should be the result of
+      // V8DOMFileSystem::IsExposed, but that's actually _nowhere_ right now.
+      // This is an attempt to preserve things that might be working while
+      // someone with actual file system API expertise looks into it.
+      return execution_context->IsWindow() ||
+             execution_context->IsWorkerGlobalScope();
+    case kFileSystemFileHandleTag:
+      return V8FileSystemFileHandle::IsExposed(execution_context);
+    case kFileSystemDirectoryHandleTag:
+      return V8FileSystemDirectoryHandle::IsExposed(execution_context);
+    case kRTCCertificateTag:
+      return V8RTCCertificate::IsExposed(execution_context);
+    case kRTCEncodedAudioFrameTag:
+      return V8RTCEncodedAudioFrame::IsExposed(execution_context);
+    case kRTCEncodedVideoFrameTag:
+      return V8RTCEncodedVideoFrame::IsExposed(execution_context);
+    case kAudioDataTag:
+      return V8AudioData::IsExposed(execution_context);
+    case kVideoFrameTag:
+      return V8VideoFrame::IsExposed(execution_context);
+    case kEncodedAudioChunkTag:
+      return V8EncodedAudioChunk::IsExposed(execution_context);
+    case kEncodedVideoChunkTag:
+      return V8EncodedVideoChunk::IsExposed(execution_context);
+    case kMediaStreamTrack:
+      return V8MediaStreamTrack::IsExposed(execution_context);
+    case kCropTargetTag:
+      return V8CropTarget::IsExposed(execution_context);
+    case kMediaSourceHandleTag:
+      return V8MediaSourceHandle::IsExposed(execution_context);
+    default:
+      return false;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
index ab3e3220..1473fca 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
@@ -39,6 +39,9 @@
       const Options& options = Options())
       : V8ScriptValueDeserializer(script_state, std::move(value), options) {}
 
+  static bool ExecutionContextExposesInterface(ExecutionContext*,
+                                               SerializationTag interface_tag);
+
  protected:
   ScriptWrappable* ReadDOMObject(SerializationTag, ExceptionState&) override;
 
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
index a04f5878..21a91c57 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -184,7 +184,7 @@
           "A FileSystem object could not be cloned.");
       return false;
     }
-    WriteTag(kDOMFileSystemTag);
+    WriteAndRequireInterfaceTag(kDOMFileSystemTag);
     // This locks in the values of the FileSystemType enumerators.
     WriteUint32(static_cast<uint32_t>(fs->GetType()));
     WriteUTF8String(fs->name());
@@ -208,7 +208,7 @@
   }
   if (auto* certificate = dispatcher.ToMostDerived<RTCCertificate>()) {
     rtc::RTCCertificatePEM pem = certificate->Certificate()->ToPEM();
-    WriteTag(kRTCCertificateTag);
+    WriteAndRequireInterfaceTag(kRTCCertificateTag);
     WriteUTF8String(pem.private_key().c_str());
     WriteUTF8String(pem.certificate().c_str());
     return true;
@@ -455,7 +455,7 @@
 bool V8ScriptValueSerializerForModules::WriteCryptoKey(
     const WebCryptoKey& key,
     ExceptionState& exception_state) {
-  WriteTag(kCryptoKeyTag);
+  WriteAndRequireInterfaceTag(kCryptoKeyTag);
 
   // Write params.
   const WebCryptoKeyAlgorithm& algorithm = key.Algorithm();
@@ -541,7 +541,7 @@
   tokens_array.push_back(std::move(token));
   const uint32_t token_index = static_cast<uint32_t>(tokens_array.size() - 1);
 
-  WriteTag(tag);
+  WriteAndRequireInterfaceTag(tag);
   WriteUTF8String(file_system_handle->name());
   WriteUint32(token_index);
   return true;
@@ -556,7 +556,7 @@
   frames.push_back(audio_frame->Delegate());
   const uint32_t index = static_cast<uint32_t>(frames.size() - 1);
 
-  WriteTag(kRTCEncodedAudioFrameTag);
+  WriteAndRequireInterfaceTag(kRTCEncodedAudioFrameTag);
   WriteUint32(index);
   return true;
 }
@@ -570,7 +570,7 @@
   frames.push_back(video_frame->Delegate());
   const uint32_t index = static_cast<uint32_t>(frames.size() - 1);
 
-  WriteTag(kRTCEncodedVideoFrameTag);
+  WriteAndRequireInterfaceTag(kRTCEncodedVideoFrameTag);
   WriteUint32(index);
   return true;
 }
@@ -583,7 +583,7 @@
   frames.push_back(std::move(handle));
   const uint32_t index = static_cast<uint32_t>(frames.size() - 1);
 
-  WriteTag(kVideoFrameTag);
+  WriteAndRequireInterfaceTag(kVideoFrameTag);
   WriteUint32(index);
 
   return true;
@@ -597,7 +597,7 @@
   audio_buffers.push_back(std::move(audio_data));
   const uint32_t index = static_cast<uint32_t>(audio_buffers.size() - 1);
 
-  WriteTag(kAudioDataTag);
+  WriteAndRequireInterfaceTag(kAudioDataTag);
   WriteUint32(index);
 
   return true;
@@ -612,7 +612,8 @@
   buffers.push_back(std::move(data));
   const uint32_t index = static_cast<uint32_t>(buffers.size() - 1);
 
-  WriteTag(for_audio ? kEncodedAudioChunkTag : kEncodedVideoChunkTag);
+  WriteAndRequireInterfaceTag(for_audio ? kEncodedAudioChunkTag
+                                        : kEncodedVideoChunkTag);
   WriteUint32(index);
 
   return true;
@@ -639,7 +640,7 @@
   // interface.
   auto transfer_id = base::UnguessableToken::Create();
 
-  WriteTag(kMediaStreamTrack);
+  WriteAndRequireInterfaceTag(kMediaStreamTrack);
   auto track_impl_subtype = SerializeTrackImplSubtype(dispatcher);
   WriteUint32Enum(track_impl_subtype);
   WriteUnguessableToken(*device->serializable_session_id());
@@ -685,7 +686,7 @@
 
 bool V8ScriptValueSerializerForModules::WriteCropTarget(
     CropTarget* crop_target) {
-  WriteTag(kCropTargetTag);
+  WriteAndRequireInterfaceTag(kCropTargetTag);
   WriteUTF8String(crop_target->GetCropId());
   return true;
 }
@@ -730,7 +731,7 @@
   handle->mark_serialized();
   const uint32_t index = static_cast<uint32_t>(attachments.size() - 1);
 
-  WriteTag(kMediaSourceHandleTag);
+  WriteAndRequireInterfaceTag(kMediaSourceHandleTag);
   WriteUint32(index);
 
   return true;
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 0cfc220..693e42b0 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -438,7 +438,7 @@
 
 // AES-128-CBC uses AES key params.
 TEST(V8ScriptValueSerializerForModulesTest, RoundTripCryptoKeyAES) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Generate a 128-bit AES key.
@@ -479,7 +479,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeCryptoKeyAES) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Decode a 128-bit AES key (non-extractable, decrypt only).
@@ -508,7 +508,7 @@
 
 // HMAC-SHA256 uses HMAC key params.
 TEST(V8ScriptValueSerializerForModulesTest, RoundTripCryptoKeyHMAC) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Generate an HMAC-SHA256 key.
@@ -548,7 +548,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeCryptoKeyHMAC) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Decode an HMAC-SHA256 key (non-extractable, verify only).
@@ -581,7 +581,7 @@
 
 // RSA-PSS-SHA256 uses RSA hashed key params.
 TEST(V8ScriptValueSerializerForModulesTest, RoundTripCryptoKeyRSAHashed) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Generate an RSA-PSS-SHA256 key pair.
@@ -623,7 +623,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeCryptoKeyRSAHashed) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Decode an RSA-PSS-SHA256 public key (extractable, verify only).
@@ -673,7 +673,7 @@
 
 // ECDSA uses EC key params.
 TEST(V8ScriptValueSerializerForModulesTest, RoundTripCryptoKeyEC) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Generate an ECDSA key pair with the NIST P-256 curve.
@@ -715,7 +715,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeCryptoKeyEC) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Decode an ECDSA public key with the NIST P-256 curve (extractable).
@@ -754,7 +754,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, RoundTripCryptoKeyNoParams) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Import some data into a PBKDF2 state.
@@ -787,7 +787,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeCryptoKeyNoParams) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Decode PBKDF2 state seeded with {1,2,3}.
@@ -815,7 +815,7 @@
 }
 
 TEST(V8ScriptValueSerializerForModulesTest, DecodeCryptoKeyInvalid) {
-  V8TestingScope scope;
+  V8TestingScope scope(KURL("https://secure.context/"));
   ScriptState* script_state = scope.GetScriptState();
 
   // Invalid algorithm ID.
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc
index e0738d3c..62c5df7 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc
@@ -169,8 +169,8 @@
 // V8 serialization code. The code below DCHECKs that
 constexpr static size_t kSSVHeaderBlinkVersionTagOffset = 0;
 constexpr static size_t kSSVHeaderBlinkVersionOffset = 1;
-constexpr static size_t kSSVHeaderV8VersionTagOffset = 2;
-// constexpr static size_t kSSVHeaderV8VersionOffset = 3;
+constexpr static size_t kSSVHeaderV8VersionTagOffset = 15;
+// constexpr static size_t kSSVHeaderV8VersionOffset = 16;
 
 // Follows the same steps as the IndexedDB value serialization code.
 void SerializeV8Value(v8::Local<v8::Value> value,
diff --git a/third_party/blink/renderer/core/events/message_event.cc b/third_party/blink/renderer/core/events/message_event.cc
index c33c2a9..43505ec 100644
--- a/third_party/blink/renderer/core/events/message_event.cc
+++ b/third_party/blink/renderer/core/events/message_event.cc
@@ -384,6 +384,12 @@
   return data_as_serialized_script_value_->Value()->IsLockedToAgentCluster();
 }
 
+bool MessageEvent::CanDeserializeIn(ExecutionContext* execution_context) const {
+  return data_type_ != kDataTypeSerializedScriptValue ||
+         data_as_serialized_script_value_->Value()->CanDeserializeIn(
+             execution_context);
+}
+
 void MessageEvent::EntangleMessagePorts(ExecutionContext* context) {
   ports_ = MessagePort::EntanglePorts(*context, std::move(channels_));
   is_ports_dirty_ = true;
diff --git a/third_party/blink/renderer/core/events/message_event.h b/third_party/blink/renderer/core/events/message_event.h
index af92c5bb..9e814f1 100644
--- a/third_party/blink/renderer/core/events/message_event.h
+++ b/third_party/blink/renderer/core/events/message_event.h
@@ -193,6 +193,10 @@
   // agent cluster.
   bool IsLockedToAgentCluster() const;
 
+  // Returns true when |data_as_serialized_script_value_| is not prevented from
+  // being deserialized in the provided execution context.
+  bool CanDeserializeIn(ExecutionContext*) const;
+
   void EntangleMessagePorts(ExecutionContext*);
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index fe5e2a6..c23f85b 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1205,6 +1205,10 @@
     }
   }
 
+  if (!event->CanDeserializeIn(this)) {
+    event = MessageEvent::CreateError(event->origin(), event->source());
+  }
+
   if (GetFrame() && GetFrame()->GetPage() &&
       GetFrame()->GetPage()->DispatchedPagehideAndStillHidden() &&
       !document()->UnloadEventInProgress()) {
diff --git a/third_party/blink/renderer/core/frame/pending_beacon.cc b/third_party/blink/renderer/core/frame/pending_beacon.cc
index dc78650..7ddcd00 100644
--- a/third_party/blink/renderer/core/frame/pending_beacon.cc
+++ b/third_party/blink/renderer/core/frame/pending_beacon.cc
@@ -28,7 +28,7 @@
 // min(Time evicted from back/forward cache,
 //     `kDefaultPendingBeaconMaxBackgroundTimeout`).
 // Note that this is currently longer than back/forward cache entry's TTL.
-// See https://github.com/WICG/unload-beacon/issues/3
+// See https://github.com/WICG/pending-beacon/issues/3
 constexpr base::TimeDelta kDefaultPendingBeaconMaxBackgroundTimeout =
     base::Minutes(30);  // 30 minutes
 
@@ -113,11 +113,11 @@
 
   // TODO(crbug.com/3774273): Use the nullity of data & url to decide whether
   // beacon should be sent.
-  // https://github.com/WICG/unload-beacon/issues/17#issuecomment-1198871880
+  // https://github.com/WICG/pending-beacon/issues/17#issuecomment-1198871880
 
   // If timeout >= 0, the timer starts immediately after its value is set or
   // updated.
-  // https://github.com/WICG/unload-beacon/blob/main/README.md#properties
+  // https://github.com/WICG/pending-beacon/blob/main/README.md#properties
   timeout_timer_.StartOneShot(timeout_, FROM_HERE);
 }
 
diff --git a/third_party/blink/renderer/core/frame/pending_beacon.h b/third_party/blink/renderer/core/frame/pending_beacon.h
index f1f00a61..5c04f0c 100644
--- a/third_party/blink/renderer/core/frame/pending_beacon.h
+++ b/third_party/blink/renderer/core/frame/pending_beacon.h
@@ -21,7 +21,7 @@
 class ExecutionContext;
 
 // Implementation of the PendingBeacon API.
-// https://github.com/WICG/unload-beacon/blob/main/README.md
+// https://github.com/WICG/pending-beacon/blob/main/README.md
 // Note that the lifetime of a PendingBeacon instance is not the same as the JS
 // scope where the instance is created. Rather, it stays alive until
 //   - roughly when `sendNow()` or `deactivate()` is called (may still be alive
diff --git a/third_party/blink/renderer/core/frame/pending_beacon.idl b/third_party/blink/renderer/core/frame/pending_beacon.idl
index e30b5d39..e08bfce 100644
--- a/third_party/blink/renderer/core/frame/pending_beacon.idl
+++ b/third_party/blink/renderer/core/frame/pending_beacon.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/unload-beacon/blob/main/README.md
+// https://github.com/WICG/pending-beacon/blob/main/README.md
 
 [RuntimeEnabled = PendingBeaconAPI] enum BeaconMethod { "POST", "GET" };
 
diff --git a/third_party/blink/renderer/core/frame/pending_beacon_dispatcher.cc b/third_party/blink/renderer/core/frame/pending_beacon_dispatcher.cc
index d725914..48bc25e 100644
--- a/third_party/blink/renderer/core/frame/pending_beacon_dispatcher.cc
+++ b/third_party/blink/renderer/core/frame/pending_beacon_dispatcher.cc
@@ -21,7 +21,7 @@
 // triggered. A bundle has beacons fall within the same 100x milliseconds.
 // Spec says: The beacon is not guaranteed to be sent at exactly this many
 // milliseconds after hidden; bundling/batching of beacons is possible.
-// https://github.com/WICG/unload-beacon/blob/main/README.md#properties
+// https://github.com/WICG/pending-beacon/blob/main/README.md#properties
 constexpr base::TimeDelta kBeaconTimeoutInterval = base::Milliseconds(100);
 
 struct ReverseBeaconTimeoutSorter {
@@ -108,7 +108,7 @@
   DCHECK(GetPage());
 
   // Handles a PendingBeacon's `backgroundTimeout` properties.
-  // https://github.com/WICG/unload-beacon/blob/main/README.md#properties
+  // https://github.com/WICG/pending-beacon/blob/main/README.md#properties
   if (GetPage()->IsPageVisible()) {
     // The timer should be reset if the page enters `visible` visibility state
     // before the `backgroundTimeout` expires.
@@ -304,7 +304,7 @@
   // after users think they have left a page, beacons queued in that page
   // still exist and get sent through the new network, which leaks navigation
   // history to the new network.
-  // See https://github.com/WICG/unload-beacon/issues/30.
+  // See https://github.com/WICG/pending-beacon/issues/30.
   //
   // Note that the pagehide event might be dispatched a bit earlier than when
   // beacons get sents by browser in same-site navigation.
diff --git a/third_party/blink/renderer/core/frame/pending_get_beacon.h b/third_party/blink/renderer/core/frame/pending_get_beacon.h
index e7ddd77..9a9860f 100644
--- a/third_party/blink/renderer/core/frame/pending_get_beacon.h
+++ b/third_party/blink/renderer/core/frame/pending_get_beacon.h
@@ -14,7 +14,7 @@
 class ExecutionContext;
 
 // Implementation of the PendingGetBeacon API.
-// https://github.com/WICG/unload-beacon/blob/main/README.md
+// https://github.com/WICG/pending-beacon/blob/main/README.md
 class CORE_EXPORT PendingGetBeacon : public PendingBeacon {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/blink/renderer/core/frame/pending_post_beacon.h b/third_party/blink/renderer/core/frame/pending_post_beacon.h
index aa744d83..6d20d59 100644
--- a/third_party/blink/renderer/core/frame/pending_post_beacon.h
+++ b/third_party/blink/renderer/core/frame/pending_post_beacon.h
@@ -16,7 +16,7 @@
 class ExecutionContext;
 
 // Implementation of the PendingPostBeacon API.
-// https://github.com/WICG/unload-beacon/blob/main/README.md
+// https://github.com/WICG/pending-beacon/blob/main/README.md
 class CORE_EXPORT PendingPostBeacon : public PendingBeacon {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
index 94ad026..04eba17 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -35,6 +35,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "cc/base/features.h"
@@ -187,6 +188,7 @@
 #include "third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/core/testing/wait_for_event.h"
 #include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
@@ -1678,6 +1680,41 @@
   RunPendingTasks();
 }
 
+TEST_F(WebFrameTest, PostMessageEvent_CannotDeserialize) {
+  frame_test_helpers::WebViewHelper web_view_helper;
+  web_view_helper.InitializeAndLoad("about:blank");
+
+  auto* frame =
+      To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
+  LocalDOMWindow* window = frame->DomWindow();
+
+  base::RunLoop run_loop;
+  auto* wait = MakeGarbageCollected<WaitForEvent>();
+  wait->AddEventListener(window, event_type_names::kMessage);
+  wait->AddEventListener(window, event_type_names::kMessageerror);
+  wait->AddCompletionClosure(run_loop.QuitClosure());
+
+  scoped_refptr<SerializedScriptValue> message =
+      SerializeString("message", ToScriptStateForMainWorld(frame));
+  SerializedScriptValue::ScopedOverrideCanDeserializeInForTesting
+      override_can_deserialize_in(base::BindLambdaForTesting(
+          [&](const SerializedScriptValue& value,
+              ExecutionContext* execution_context, bool can_deserialize) {
+            EXPECT_EQ(&value, message.get());
+            EXPECT_EQ(execution_context, window);
+            EXPECT_TRUE(can_deserialize);
+            return false;
+          }));
+
+  NonThrowableExceptionState exception_state;
+  frame->DomWindow()->PostMessageForTesting(message, MessagePortArray(), "*",
+                                            window, exception_state);
+  EXPECT_FALSE(exception_state.HadException());
+
+  run_loop.Run();
+  EXPECT_EQ(wait->GetLastEvent()->type(), event_type_names::kMessageerror);
+}
+
 namespace {
 
 // Helper function to set autosizing multipliers on a document.
diff --git a/third_party/blink/renderer/core/html/portal/portal_post_message_helper.cc b/third_party/blink/renderer/core/html/portal/portal_post_message_helper.cc
index 1b497159..a6c2c62 100644
--- a/third_party/blink/renderer/core/html/portal/portal_post_message_helper.cc
+++ b/third_party/blink/renderer/core/html/portal/portal_post_message_helper.cc
@@ -76,18 +76,23 @@
   DCHECK(source_origin->IsSameOriginWith(
       event_target->GetExecutionContext()->GetSecurityOrigin()));
 
-  UserActivation* user_activation = nullptr;
-  if (message.user_activation) {
-    user_activation = MakeGarbageCollected<UserActivation>(
-        message.user_activation->has_been_active,
-        message.user_activation->was_active);
+  ExecutionContext* context = event_target->GetExecutionContext();
+  MessageEvent* event;
+  if (message.message->CanDeserializeIn(context)) {
+    UserActivation* user_activation = nullptr;
+    if (message.user_activation) {
+      user_activation = MakeGarbageCollected<UserActivation>(
+          message.user_activation->has_been_active,
+          message.user_activation->was_active);
+    }
+    event = MessageEvent::Create(message.ports, message.message,
+                                 source_origin->ToString(), String(),
+                                 event_target, user_activation);
+    event->EntangleMessagePorts(context);
+  } else {
+    event = MessageEvent::CreateError(source_origin->ToString(), event_target);
   }
 
-  MessageEvent* event = MessageEvent::Create(
-      message.ports, message.message, source_origin->ToString(), String(),
-      event_target, user_activation);
-  event->EntangleMessagePorts(event_target->GetExecutionContext());
-
   ThreadDebugger* debugger = MainThreadDebugger::Instance();
   if (debugger)
     debugger->ExternalAsyncTaskStarted(message.sender_stack_trace_id);
diff --git a/third_party/blink/renderer/core/layout/custom_scrollbar.cc b/third_party/blink/renderer/core/layout/custom_scrollbar.cc
index cbca378..d82e355 100644
--- a/third_party/blink/renderer/core/layout/custom_scrollbar.cc
+++ b/third_party/blink/renderer/core/layout/custom_scrollbar.cc
@@ -419,7 +419,7 @@
         PhysicalOffset(part_rect.origin()));
     // The part's frame_rect is relative to the scrollbar.
     part_rect.Offset(-Location().OffsetFromOrigin());
-    part.value->SetFrameRect(LayoutRect(part_rect));
+    part.value->SetOverriddenFrameRect(LayoutRect(part_rect));
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 8960fa5..20bf54e 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -448,11 +448,6 @@
     NOT_DESTROYED();
     return LayoutRect(Location(), Size());
   }
-  void SetFrameRect(const LayoutRect& rect) {
-    NOT_DESTROYED();
-    SetLocation(rect.Location());
-    SetSize(rect.Size());
-  }
 
   // Note that those functions have their origin at this box's CSS border box.
   // As such their location doesn't account for 'top'/'left'. About its
diff --git a/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc b/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc
index c2144e84..0c61d17 100644
--- a/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc
+++ b/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc
@@ -163,6 +163,21 @@
   return ComputeHeight(visible_content_rect.height());
 }
 
+void LayoutCustomScrollbarPart::SetOverriddenFrameRect(const LayoutRect& rect) {
+  NOT_DESTROYED();
+  overridden_rect_ = rect;
+}
+
+LayoutPoint LayoutCustomScrollbarPart::Location() const {
+  NOT_DESTROYED();
+  return overridden_rect_.Location();
+}
+
+LayoutSize LayoutCustomScrollbarPart::Size() const {
+  NOT_DESTROYED();
+  return overridden_rect_.Size();
+}
+
 static LayoutUnit ComputeMargin(const Length& style_margin) {
   // TODO(crbug.com/1020913): Support subpixel layout of scrollbars and remove
   // Round() below.
diff --git a/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h b/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h
index a63a6c94e..63440c4d 100644
--- a/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h
+++ b/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h
@@ -68,6 +68,13 @@
   // available.
   int ComputeLength() const;
 
+  // Update the overridden location and size.
+  void SetOverriddenFrameRect(const LayoutRect& rect);
+  // Rerturn the overridden location set by SetOverriddenFrameRect();
+  LayoutPoint Location() const override;
+  // Rerturn the overridden size set by SetOverriddenFrameRect();
+  LayoutSize Size() const override;
+
   LayoutUnit MarginTop() const override;
   LayoutUnit MarginBottom() const override;
   LayoutUnit MarginLeft() const override;
@@ -140,7 +147,7 @@
 
   Member<ScrollableArea> scrollable_area_;
   Member<CustomScrollbar> scrollbar_;
-
+  LayoutRect overridden_rect_;
   ScrollbarPart part_;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h
index 7ffc3ed..1cefe367 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h
@@ -86,6 +86,8 @@
 
  public:
   NGGridLayoutData() = default;
+  NGGridLayoutData(NGGridLayoutData&&) = default;
+  NGGridLayoutData& operator=(NGGridLayoutData&&) = default;
 
   NGGridLayoutData(std::unique_ptr<NGGridLayoutTrackCollection> columns,
                    std::unique_ptr<NGGridLayoutTrackCollection> rows)
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 3515bab..e180588 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -89,71 +89,19 @@
 
 namespace {
 
-bool HasBlockSizeDependentGridItem(const GridItems& grid_items) {
-  for (const auto& grid_item : grid_items) {
-    if (grid_item.is_sizing_dependent_on_block_size)
-      return true;
-  }
-  return false;
-}
-
-NGGridProperties InitializeGridProperties(
-    const GridItems& grid_items,
-    const WritingMode container_writing_mode) {
-  NGGridProperties grid_properties;
-
-  for (const auto& grid_item : grid_items) {
-    grid_properties.has_baseline_column |=
-        grid_item.IsBaselineSpecifiedForDirection(kForColumns);
-    grid_properties.has_baseline_row |=
-        grid_item.IsBaselineSpecifiedForDirection(kForRows);
-    grid_properties.has_orthogonal_item |=
-        !grid_item.is_parallel_with_root_grid;
-  }
-  return grid_properties;
-}
-
-void CacheGridTrackSpanProperties(
+void CacheGridItemsTrackSpanProperties(
     const NGGridLayoutTrackCollection& track_collection,
-    GridItems* grid_items,
-    NGGridProperties* grid_properties = nullptr) {
+    GridItems* grid_items) {
   DCHECK(grid_items);
 
+  GridItems grid_items_spanning_multiple_ranges;
   const auto track_direction = track_collection.Direction();
 
-  if (grid_properties) {
-    auto& track_properties = (track_direction == kForColumns)
-                                 ? grid_properties->column_properties
-                                 : grid_properties->row_properties;
-    track_properties.Reset();
-
-    const wtf_size_t range_count = track_collection.RangeCount();
-    auto CacheGridProperty =
-        [&](const TrackSpanProperties::PropertyId property) {
-          for (wtf_size_t i = 0; i < range_count; ++i) {
-            if (track_collection.RangeHasTrackSpanProperty(i, property)) {
-              track_properties.SetProperty(property);
-              break;
-            }
-          }
-        };
-
-    CacheGridProperty(TrackSpanProperties::kHasFlexibleTrack);
-    CacheGridProperty(TrackSpanProperties::kHasIntrinsicTrack);
-    CacheGridProperty(TrackSpanProperties::kHasNonDefiniteTrack);
-    CacheGridProperty(TrackSpanProperties::kIsDependentOnAvailableSize);
-  }
-
-  auto CacheTrackSpanProperty =
-      [&](GridItemData& grid_item, const wtf_size_t range_index,
-          const TrackSpanProperties::PropertyId property) {
-        if (track_collection.RangeHasTrackSpanProperty(range_index, property))
-          grid_item.SetTrackSpanProperty(property, track_direction);
-      };
-
-  GridItems grid_items_spanning_multiple_ranges;
   for (auto& grid_item : *grid_items) {
-    const auto& range_indices = grid_item.RangeIndices(track_direction);
+    auto& grid_item_properties = (track_direction == kForColumns)
+                                     ? grid_item.column_span_properties
+                                     : grid_item.row_span_properties;
+    grid_item_properties.Reset();
 
     // If a grid item spans only one range, then we can just cache the track
     // span properties directly. On the contrary, if a grid item spans multiple
@@ -161,17 +109,10 @@
     // to do more work to cache its track span properties.
     //
     // TODO(layout-dev): Investigate applying this concept to spans > 1.
+    const auto& range_indices = grid_item.RangeIndices(track_direction);
     if (range_indices.begin == range_indices.end) {
-      CacheTrackSpanProperty(grid_item, range_indices.begin,
-                             TrackSpanProperties::kHasFlexibleTrack);
-      CacheTrackSpanProperty(grid_item, range_indices.begin,
-                             TrackSpanProperties::kHasIntrinsicTrack);
-      CacheTrackSpanProperty(grid_item, range_indices.begin,
-                             TrackSpanProperties::kHasAutoMinimumTrack);
-      CacheTrackSpanProperty(grid_item, range_indices.begin,
-                             TrackSpanProperties::kHasFixedMinimumTrack);
-      CacheTrackSpanProperty(grid_item, range_indices.begin,
-                             TrackSpanProperties::kHasFixedMaximumTrack);
+      grid_item_properties |=
+          track_collection.RangeProperties(range_indices.begin);
     } else {
       grid_items_spanning_multiple_ranges.Append(&grid_item);
     }
@@ -189,7 +130,7 @@
             grid_items_spanning_multiple_ranges.item_data.end(),
             CompareGridItemsByStartLine);
 
-  auto CacheTrackSpanPropertyForAllGridItems =
+  auto CacheGridItemsSpanningMultipleRangesProperty =
       [&](const TrackSpanProperties::PropertyId property) {
         // At this point we have the remaining grid items sorted by start line
         // in the respective direction; this is important since we'll process
@@ -211,8 +152,8 @@
           while (current_range_index < range_count &&
                  (track_collection.RangeEndLine(current_range_index) <=
                       grid_item.StartLine(track_direction) ||
-                  !track_collection.RangeHasTrackSpanProperty(
-                      current_range_index, property))) {
+                  !track_collection.RangeProperties(current_range_index)
+                       .HasProperty(property))) {
             ++current_range_index;
           }
 
@@ -222,14 +163,14 @@
             break;
 
           // Notice that, from the way we build the ranges of a track collection
-          // (see |NGGridBlockTrackCollection::EnsureTrackCoverage|), any given
-          // range must either be completely contained or excluded from a grid
-          // item's span. Thus, if the current range's last track is also
-          // located BEFORE the item's end line, then this range, including a
-          // track that fulfills the specified property, is completely contained
-          // within this item's boundaries. Otherwise, this and every subsequent
-          // range are excluded from the grid item's span, meaning that such
-          // item cannot satisfy the property we are looking for.
+          // (see |NGGridRangeBuilder::EnsureTrackCoverage|), any given range
+          // must either be completely contained or excluded from a grid item's
+          // span. Thus, if the current range's last track is also located
+          // BEFORE the item's end line, then this range, including a track that
+          // fulfills the specified property, is completely contained within
+          // this item's boundaries. Otherwise, this and every subsequent range
+          // are excluded from the grid item's span, meaning that such item
+          // cannot satisfy the property we are looking for.
           if (track_collection.RangeEndLine(current_range_index) <=
               grid_item.EndLine(track_direction)) {
             grid_item.SetTrackSpanProperty(property, track_direction);
@@ -237,17 +178,49 @@
         }
       };
 
-  CacheTrackSpanPropertyForAllGridItems(TrackSpanProperties::kHasFlexibleTrack);
-  CacheTrackSpanPropertyForAllGridItems(
+  CacheGridItemsSpanningMultipleRangesProperty(
+      TrackSpanProperties::kHasFlexibleTrack);
+  CacheGridItemsSpanningMultipleRangesProperty(
       TrackSpanProperties::kHasIntrinsicTrack);
-  CacheTrackSpanPropertyForAllGridItems(
+  CacheGridItemsSpanningMultipleRangesProperty(
       TrackSpanProperties::kHasAutoMinimumTrack);
-  CacheTrackSpanPropertyForAllGridItems(
+  CacheGridItemsSpanningMultipleRangesProperty(
       TrackSpanProperties::kHasFixedMinimumTrack);
-  CacheTrackSpanPropertyForAllGridItems(
+  CacheGridItemsSpanningMultipleRangesProperty(
       TrackSpanProperties::kHasFixedMaximumTrack);
 }
 
+void CacheGridItemsProperties(const NGGridLayoutData& layout_data,
+                              GridItems* grid_items,
+                              NGGridProperties* grid_properties = nullptr) {
+  DCHECK(grid_items);
+
+  for (auto& grid_item : *grid_items) {
+    grid_item.ComputeSetIndices(*layout_data.Columns());
+    grid_item.ComputeSetIndices(*layout_data.Rows());
+
+    if (grid_properties) {
+      grid_properties->has_baseline_column |=
+          grid_item.IsBaselineSpecifiedForDirection(kForColumns);
+      grid_properties->has_baseline_row |=
+          grid_item.IsBaselineSpecifiedForDirection(kForRows);
+      grid_properties->has_orthogonal_item |=
+          !grid_item.is_parallel_with_root_grid;
+    }
+  }
+
+  CacheGridItemsTrackSpanProperties(*layout_data.Columns(), grid_items);
+  CacheGridItemsTrackSpanProperties(*layout_data.Rows(), grid_items);
+}
+
+bool HasBlockSizeDependentGridItem(const GridItems& grid_items) {
+  for (const auto& grid_item : grid_items) {
+    if (grid_item.is_sizing_dependent_on_block_size)
+      return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 const NGLayoutResult* NGGridLayoutAlgorithm::Layout() {
@@ -270,11 +243,10 @@
   auto grid_sizing_tree = IsResumingLayout(BreakToken())
                               ? BuildGridSizingTree()
                               : BuildGridSizingTree(&oof_children);
-  auto& sizing_data = grid_sizing_tree[0];
 
   LayoutUnit intrinsic_block_size;
-  auto& grid_items = sizing_data.grid_items;
-  auto& layout_data = sizing_data.layout_data;
+  auto& grid_items = grid_sizing_tree[0].grid_items;
+  auto& layout_data = grid_sizing_tree[0].layout_data;
 
   if (IsResumingLayout(BreakToken())) {
     // TODO(layout-dev): When we support variable inline-size fragments we'll
@@ -287,16 +259,9 @@
     intrinsic_block_size = grid_data->intrinsic_block_size;
     layout_data = grid_data->layout_data;
 
-    for (auto& grid_item : grid_items) {
-      grid_item.ComputeSetIndices(*layout_data.Columns());
-      grid_item.ComputeSetIndices(*layout_data.Rows());
-    }
-
-    CacheGridTrackSpanProperties(*layout_data.Columns(), &grid_items);
-    CacheGridTrackSpanProperties(*layout_data.Rows(), &grid_items);
+    CacheGridItemsProperties(layout_data, &grid_items);
   } else {
-    ComputeGridGeometry(node.CachedPlacementData(), &grid_items, &layout_data,
-                        &intrinsic_block_size);
+    ComputeGridGeometry(&grid_sizing_tree, &intrinsic_block_size);
   }
 
   // Subgridded items must be placed by their parent.
@@ -438,28 +403,18 @@
         node.CachedPlacementData().row_start_offset;
   }
 
+  NGGridProperties grid_properties;
   NGGridLayoutData layout_data(
       LayoutTrackCollection(placement_data, kForColumns, &grid_items),
       LayoutTrackCollection(placement_data, kForRows, &grid_items));
 
-  for (auto& grid_item : grid_items) {
-    grid_item.ComputeSetIndices(*layout_data.Columns());
-    grid_item.ComputeSetIndices(*layout_data.Rows());
-  }
-
-  auto grid_properties =
-      InitializeGridProperties(grid_items, Style().GetWritingMode());
-
-  CacheGridTrackSpanProperties(*layout_data.Columns(), &grid_items,
-                               &grid_properties);
-  CacheGridTrackSpanProperties(*layout_data.Rows(), &grid_items,
-                               &grid_properties);
-
   bool depends_on_block_constraints = false;
   auto ComputeTotalColumnSize =
       [&](const SizingConstraint sizing_constraint) -> LayoutUnit {
-    InitializeTrackSizes(grid_properties, layout_data.Columns());
-    InitializeTrackSizes(grid_properties, layout_data.Rows());
+    InitializeTrackSizes(layout_data.Columns(), &grid_properties);
+    InitializeTrackSizes(layout_data.Rows(), &grid_properties);
+
+    CacheGridItemsProperties(layout_data, &grid_items, &grid_properties);
 
     bool needs_additional_pass = false;
     ComputeUsedTrackSizes(layout_data, grid_properties, sizing_constraint,
@@ -478,7 +433,7 @@
                               &needs_additional_pass);
 
         if (needs_additional_pass) {
-          InitializeTrackSizes(grid_properties, layout_data.Columns());
+          InitializeTrackSizes(layout_data.Columns(), &grid_properties);
           ComputeUsedTrackSizes(layout_data, grid_properties, sizing_constraint,
                                 &grid_items, layout_data.Columns());
         }
@@ -507,13 +462,13 @@
   const auto& style = node.Style();
 
   bool has_nested_subgrid;
-
   auto& sizing_data = sizing_tree->CreateSizingData(node, parent_sizing_data,
                                                     subgrid_data_in_parent);
   {
     GridArea subgridded_area_in_parent;
     if (subgrid_data_in_parent) {
       subgridded_area_in_parent = subgrid_data_in_parent->resolved_position;
+
       if (!subgrid_data_in_parent->is_parallel_with_root_grid) {
         std::swap(subgridded_area_in_parent.columns,
                   subgridded_area_in_parent.rows);
@@ -545,56 +500,70 @@
         placement_data, oof_children, &has_nested_subgrid);
   }
 
-  if (has_nested_subgrid) {
-    // |AppendSubgriddedItems| rely on the cached placement data of a subgrid to
-    // construct its grid items, so we need to build their subtrees beforehand.
-    const auto* line_resolver = &node.CachedPlacementData().line_resolver;
-    for (const auto& grid_item : sizing_data.grid_items) {
-      if (!grid_item.IsSubgrid())
-        continue;
+  const bool must_build_layout_data_for_columns =
+      sizing_data.MustBuildLayoutData(kForColumns);
+  const bool must_build_layout_data_for_rows =
+      sizing_data.MustBuildLayoutData(kForRows);
+  const auto& placement_data = node.CachedPlacementData();
 
-      // Create a grid algorithm using an indefinite constraint space.
-      const auto space = CreateConstraintSpace(
-          grid_item, NGGridLayoutData(), {kIndefiniteSize, kIndefiniteSize},
-          NGCacheSlot::kLayout,
-          /* opt_fixed_block_size */ absl::nullopt);
-      const auto fragment_geometry = CalculateInitialFragmentGeometry(
-          space, grid_item.node, /* break_token */ nullptr,
-          /* is_intrinsic */ true);
-      NGGridLayoutAlgorithm algorithm(
-          {grid_item.node, fragment_geometry, space});
+  auto BuildLayoutData = [&](GridTrackSizingDirection track_direction) {
+    NGGridRangeBuilder range_builder(style, placement_data, track_direction);
 
-      sizing_data.subtree_size += algorithm.BuildGridSizingSubtree(
-          sizing_tree, /* oof_children */ nullptr, &sizing_data, line_resolver,
-          &grid_item);
+    for (auto& grid_item : sizing_data.grid_items) {
+      auto& range_indices = grid_item.RangeIndices(track_direction);
+      range_builder.EnsureTrackCoverage(grid_item.StartLine(track_direction),
+                                        grid_item.SpanSize(track_direction),
+                                        &range_indices.begin,
+                                        &range_indices.end);
     }
-    node.AppendSubgriddedItems(&sizing_data.grid_items);
+
+    auto& sizing_collection = (track_direction == kForColumns)
+                                  ? sizing_data.layout_data.columns
+                                  : sizing_data.layout_data.rows;
+
+    sizing_collection = std::make_unique<NGGridSizingTrackCollection>(
+        range_builder.FinalizeRanges(), track_direction);
+  };
+
+  if (must_build_layout_data_for_columns)
+    BuildLayoutData(kForColumns);
+  if (must_build_layout_data_for_rows)
+    BuildLayoutData(kForRows);
+
+  if (!has_nested_subgrid)
+    return sizing_data.subtree_size;
+
+  // |AppendSubgriddedItems| rely on the cached placement data of a subgrid to
+  // construct its grid items, so we need to build their subtrees beforehand.
+  for (const auto& grid_item : sizing_data.grid_items) {
+    if (!grid_item.IsSubgrid())
+      continue;
+
+    // Create a grid algorithm using an indefinite constraint space.
+    const auto space = CreateConstraintSpace(
+        grid_item, NGGridLayoutData(), {kIndefiniteSize, kIndefiniteSize},
+        NGCacheSlot::kLayout,
+        /* opt_fixed_block_size */ absl::nullopt);
+    const auto fragment_geometry = CalculateInitialFragmentGeometry(
+        space, grid_item.node, /* break_token */ nullptr,
+        /* is_intrinsic */ true);
+    NGGridLayoutAlgorithm algorithm({grid_item.node, fragment_geometry, space});
+
+    sizing_data.subtree_size += algorithm.BuildGridSizingSubtree(
+        sizing_tree, /* oof_children */ nullptr, &sizing_data,
+        &placement_data.line_resolver, &grid_item);
   }
 
-  auto FinalizeTrackBuilderCollection =
-      [&](NGGridBlockTrackCollection* track_collection) {
-        const auto track_direction = track_collection->Direction();
+  node.AppendSubgriddedItems(&sizing_data.grid_items);
 
-        for (auto& grid_item : sizing_data.grid_items) {
-          const auto& item_span = grid_item.Span(track_direction);
-          auto& range_indices = grid_item.RangeIndices(track_direction);
-
-          track_collection->EnsureTrackCoverage(
-              item_span.StartLine(), item_span.IntegerSpan(),
-              &range_indices.begin, &range_indices.end);
-        }
-        track_collection->FinalizeRanges();
-      };
-
-  sizing_data.column_builder_collection =
-      std::make_unique<NGGridBlockTrackCollection>(
-          style, node.CachedPlacementData(), kForColumns);
-  FinalizeTrackBuilderCollection(sizing_data.column_builder_collection.get());
-
-  sizing_data.row_builder_collection =
-      std::make_unique<NGGridBlockTrackCollection>(
-          style, node.CachedPlacementData(), kForRows);
-  FinalizeTrackBuilderCollection(sizing_data.row_builder_collection.get());
+  // We need to recreate the track builder collections to ensure track coverage
+  // for subgridded items; it would be ideal to have them accounted for already,
+  // but we might need the track collections to compute a subgrid's automatic
+  // repetitions, so we do this process twice to avoid a cyclic dependency.
+  if (must_build_layout_data_for_columns)
+    BuildLayoutData(kForColumns);
+  if (must_build_layout_data_for_rows)
+    BuildLayoutData(kForRows);
 
   return sizing_data.subtree_size;
 }
@@ -603,11 +572,18 @@
     HeapVector<Member<LayoutBox>>* oof_children) const {
   NGGridSizingTree sizing_tree;
 
-  // For subgrids, we should already have their cached placement data, so we can
-  // rely on it to directly build the grid items of this subgrid.
-  if (ConstraintSpace().SubgriddedColumns() ||
-      ConstraintSpace().SubgriddedRows()) {
+  const auto& constraint_space = ConstraintSpace();
+  const auto* subgridded_columns = constraint_space.SubgriddedColumns();
+  const auto* subgridded_rows = constraint_space.SubgriddedRows();
+
+  // For subgrids, we build only the direct children and rely on the subgridded
+  // tracks from the constraint space to build layout data. This isn't ideal for
+  // the grid sizing tree approach, but we do it to keep passing subgrid tests.
+  //
+  // TODO(ethavar): Remove all of this redundant code.
+  if (subgridded_columns || subgridded_rows) {
     const auto& node = Node();
+    const auto& container_style = Style();
     auto& sizing_data = sizing_tree.CreateSizingData(node, nullptr, nullptr);
 
     bool has_nested_subgrid;
@@ -615,6 +591,44 @@
         node.CachedPlacementData(), oof_children, &has_nested_subgrid);
     if (has_nested_subgrid)
       node.AppendSubgriddedItems(&sizing_data.grid_items);
+
+    auto BuildLayoutData = [&](GridTrackSizingDirection track_direction) {
+      NGGridRangeBuilder range_builder(
+          container_style, node.CachedPlacementData(), track_direction);
+
+      for (auto& grid_item : sizing_data.grid_items) {
+        auto& range_indices = grid_item.RangeIndices(track_direction);
+        range_builder.EnsureTrackCoverage(grid_item.StartLine(track_direction),
+                                          grid_item.SpanSize(track_direction),
+                                          &range_indices.begin,
+                                          &range_indices.end);
+      }
+
+      auto& sizing_collection = (track_direction == kForColumns)
+                                    ? sizing_data.layout_data.columns
+                                    : sizing_data.layout_data.rows;
+
+      sizing_collection = std::make_unique<NGGridSizingTrackCollection>(
+          range_builder.FinalizeRanges(), track_direction);
+    };
+
+    if (subgridded_columns) {
+      sizing_data.layout_data.columns =
+          std::make_unique<NGGridLayoutTrackCollection>(
+              *subgridded_columns, BorderScrollbarPadding(),
+              ComputeMarginsForSelf(constraint_space, container_style));
+    } else {
+      BuildLayoutData(kForColumns);
+    }
+
+    if (subgridded_rows) {
+      sizing_data.layout_data.rows =
+          std::make_unique<NGGridLayoutTrackCollection>(
+              *subgridded_rows, BorderScrollbarPadding(),
+              ComputeMarginsForSelf(constraint_space, container_style));
+    } else {
+      BuildLayoutData(kForRows);
+    }
     return sizing_tree;
   }
 
@@ -770,11 +784,9 @@
 }  // namespace
 
 void NGGridLayoutAlgorithm::ComputeGridGeometry(
-    const NGGridPlacementData& placement_data,
-    GridItems* grid_items,
-    NGGridLayoutData* layout_data,
+    NGGridSizingTree* grid_sizing_tree,
     LayoutUnit* intrinsic_block_size) {
-  DCHECK(grid_items && layout_data && intrinsic_block_size);
+  DCHECK(grid_sizing_tree && intrinsic_block_size);
 
   const auto& node = Node();
   const auto& container_style = Style();
@@ -782,61 +794,34 @@
   const auto& border_scrollbar_padding = BorderScrollbarPadding();
 
   DCHECK_NE(grid_available_size_.inline_size, kIndefiniteSize);
-  layout_data->columns =
-      LayoutTrackCollection(placement_data, kForColumns, grid_items);
 
-  // Build the rows manually, since we might need the builder collection later
-  // if we need to recompute the grid with its resolved block size.
-  std::unique_ptr<NGGridBlockTrackCollection> row_builder_collection;
+  auto& root_sizing_data = (*grid_sizing_tree)[0];
+  auto& grid_items = root_sizing_data.grid_items;
+  auto& grid_properties = root_sizing_data.grid_properties;
+  auto& layout_data = root_sizing_data.layout_data;
 
-  if (const auto* subgridded_rows = constraint_space.SubgriddedRows()) {
-    layout_data->rows = std::make_unique<NGGridLayoutTrackCollection>(
-        *subgridded_rows, border_scrollbar_padding,
-        ComputeMarginsForSelf(constraint_space, container_style));
-  } else {
-    row_builder_collection = std::make_unique<NGGridBlockTrackCollection>(
-        container_style, placement_data, kForRows);
-    BuildBlockTrackCollection(grid_items, row_builder_collection.get());
+  InitializeTrackSizes(layout_data.Columns(), &grid_properties);
+  InitializeTrackSizes(layout_data.Rows(), &grid_properties);
 
-    const bool is_block_available_size_indefinite =
-        grid_available_size_.block_size == kIndefiniteSize;
-
-    layout_data->rows = std::make_unique<NGGridSizingTrackCollection>(
-        *row_builder_collection, is_block_available_size_indefinite);
-  }
-
-  for (auto& grid_item : *grid_items) {
-    grid_item.ComputeSetIndices(*layout_data->Columns());
-    grid_item.ComputeSetIndices(*layout_data->Rows());
-  }
-
-  auto grid_properties =
-      InitializeGridProperties(*grid_items, container_style.GetWritingMode());
-
-  CacheGridTrackSpanProperties(*layout_data->Columns(), grid_items,
-                               &grid_properties);
-  CacheGridTrackSpanProperties(*layout_data->Rows(), grid_items,
-                               &grid_properties);
-  InitializeTrackSizes(grid_properties, layout_data->Columns());
-  InitializeTrackSizes(grid_properties, layout_data->Rows());
+  CacheGridItemsProperties(layout_data, &grid_items, &grid_properties);
 
   bool needs_additional_pass = false;
-  ComputeUsedTrackSizes(*layout_data, grid_properties,
-                        SizingConstraint::kLayout, grid_items,
-                        layout_data->Columns(), &needs_additional_pass);
-  ComputeUsedTrackSizes(*layout_data, grid_properties,
-                        SizingConstraint::kLayout, grid_items,
-                        layout_data->Rows(), &needs_additional_pass);
+  ComputeUsedTrackSizes(layout_data, grid_properties, SizingConstraint::kLayout,
+                        &grid_items, layout_data.Columns(),
+                        &needs_additional_pass);
+  ComputeUsedTrackSizes(layout_data, grid_properties, SizingConstraint::kLayout,
+                        &grid_items, layout_data.Rows(),
+                        &needs_additional_pass);
 
   if (contain_intrinsic_block_size_) {
     *intrinsic_block_size = *contain_intrinsic_block_size_;
   } else {
-    *intrinsic_block_size = layout_data->Rows()->ComputeSetSpanSize() +
+    *intrinsic_block_size = layout_data.Rows()->ComputeSetSpanSize() +
                             border_scrollbar_padding.BlockSum();
 
     // TODO(layout-dev): This isn't great but matches legacy. Ideally this
     // would only apply when we have only flexible track(s).
-    if (grid_items->IsEmpty() && node.HasLineIfEmpty()) {
+    if (grid_items.IsEmpty() && node.HasLineIfEmpty()) {
       *intrinsic_block_size = std::max(
           *intrinsic_block_size, border_scrollbar_padding.BlockSum() +
                                      node.EmptyLineBlockSize(BreakToken()));
@@ -847,7 +832,7 @@
         *intrinsic_block_size);
   }
 
-  if (layout_data->Rows()->IsForSizing() &&
+  if (layout_data.Rows()->IsForSizing() &&
       grid_available_size_.block_size == kIndefiniteSize) {
     const auto block_size = ComputeBlockSizeForFragment(
         constraint_space, container_style, BorderPadding(),
@@ -890,7 +875,7 @@
         container_style.AlignContent() !=
             ComputedStyleInitialValues::InitialAlignContent()) {
       auto& sizing_collection =
-          To<NGGridSizingTrackCollection>(*layout_data->Rows());
+          To<NGGridSizingTrackCollection>(*layout_data.Rows());
 
       // Re-compute the row geometry now that we resolved the available block
       // size. "align-content: space-evenly", etc, require the resolved size.
@@ -904,37 +889,34 @@
   }
 
   if (needs_additional_pass) {
-    DCHECK(row_builder_collection);
+    InitializeTrackSizes(layout_data.Columns(), &grid_properties);
+    CacheGridItemsTrackSpanProperties(*layout_data.Columns(), &grid_items);
 
-    InitializeTrackSizes(grid_properties, layout_data->Columns());
-    ComputeUsedTrackSizes(*layout_data, grid_properties,
-                          SizingConstraint::kLayout, grid_items,
-                          layout_data->Columns());
+    ComputeUsedTrackSizes(layout_data, grid_properties,
+                          SizingConstraint::kLayout, &grid_items,
+                          layout_data.Columns());
 
-    layout_data->rows = std::make_unique<NGGridSizingTrackCollection>(
-        *row_builder_collection, /* is_available_size_indefinite */ false);
-    CacheGridTrackSpanProperties(*layout_data->Rows(), grid_items,
-                                 &grid_properties);
+    InitializeTrackSizes(layout_data.Rows(), &grid_properties);
+    CacheGridItemsTrackSpanProperties(*layout_data.Rows(), &grid_items);
 
-    InitializeTrackSizes(grid_properties, layout_data->Rows());
-    ComputeUsedTrackSizes(*layout_data, grid_properties,
-                          SizingConstraint::kLayout, grid_items,
-                          layout_data->Rows());
+    ComputeUsedTrackSizes(layout_data, grid_properties,
+                          SizingConstraint::kLayout, &grid_items,
+                          layout_data.Rows());
   }
 
   // Calculate final alignment baselines for grid item layout.
   if (grid_properties.HasBaseline(kForColumns) &&
-      layout_data->Columns()->IsForSizing()) {
+      layout_data.Columns()->IsForSizing()) {
     CalculateAlignmentBaselines(
-        *layout_data, SizingConstraint::kLayout, grid_items,
-        To<NGGridSizingTrackCollection>(layout_data->Columns()));
+        layout_data, SizingConstraint::kLayout, &grid_items,
+        To<NGGridSizingTrackCollection>(layout_data.Columns()));
   }
 
   if (grid_properties.HasBaseline(kForRows) &&
-      layout_data->Rows()->IsForSizing()) {
+      layout_data.Rows()->IsForSizing()) {
     CalculateAlignmentBaselines(
-        *layout_data, SizingConstraint::kLayout, grid_items,
-        To<NGGridSizingTrackCollection>(layout_data->Rows()));
+        layout_data, SizingConstraint::kLayout, &grid_items,
+        To<NGGridSizingTrackCollection>(layout_data.Rows()));
   }
 }
 
@@ -956,12 +938,8 @@
   layout_data.rows =
       LayoutTrackCollection(PlacementData(), kForRows, &grid_items);
 
-  auto grid_properties =
-      InitializeGridProperties(grid_items, Style().GetWritingMode());
-  CacheGridTrackSpanProperties(*layout_data.Rows(), &grid_items,
-                               &grid_properties);
-
-  InitializeTrackSizes(grid_properties, layout_data.Rows());
+  NGGridProperties grid_properties;
+  InitializeTrackSizes(layout_data.Rows(), &grid_properties);
   ComputeUsedTrackSizes(layout_data, grid_properties, SizingConstraint::kLayout,
                         &grid_items, layout_data.Rows());
 
@@ -1340,16 +1318,13 @@
         ComputeMarginsForSelf(constraint_space, container_style));
   }
 
-  NGGridBlockTrackCollection track_builder_collection(
-      container_style, placement_data, track_direction);
-  BuildBlockTrackCollection(grid_items, &track_builder_collection);
-
-  const bool is_available_size_indefinite =
-      (is_for_columns ? grid_available_size_.inline_size
-                      : grid_available_size_.block_size) == kIndefiniteSize;
+  NGGridRangeBuilder track_builder_collection(container_style, placement_data,
+                                              track_direction);
+  BuildBlockTrackCollection(track_direction, grid_items,
+                            &track_builder_collection);
 
   return std::make_unique<NGGridSizingTrackCollection>(
-      track_builder_collection, is_available_size_indefinite);
+      track_builder_collection.FinalizeRanges(), track_direction);
 }
 
 // https://drafts.csswg.org/css-grid-2/#auto-repeat
@@ -1469,12 +1444,11 @@
 }
 
 void NGGridLayoutAlgorithm::BuildBlockTrackCollection(
+    GridTrackSizingDirection track_direction,
     GridItems* grid_items,
-    NGGridBlockTrackCollection* track_collection) const {
+    NGGridRangeBuilder* track_collection) const {
   DCHECK(grid_items && track_collection);
 
-  const auto track_direction = track_collection->Direction();
-
   for (auto& grid_item : *grid_items) {
     auto& range_indices = grid_item.RangeIndices(track_direction);
     track_collection->EnsureTrackCoverage(grid_item.StartLine(track_direction),
@@ -1482,7 +1456,6 @@
                                           &range_indices.begin,
                                           &range_indices.end);
   }
-  track_collection->FinalizeRanges();
 }
 
 void NGGridLayoutAlgorithm::CalculateAlignmentBaselines(
@@ -1557,69 +1530,25 @@
 
 // https://drafts.csswg.org/css-grid-2/#algo-init
 void NGGridLayoutAlgorithm::InitializeTrackSizes(
-    const NGGridProperties& grid_properties,
-    NGGridLayoutTrackCollection* track_collection) const {
+    NGGridLayoutTrackCollection* track_collection,
+    NGGridProperties* grid_properties) const {
   DCHECK(track_collection);
 
   // The track collection is not being sized by this grid container.
   if (!track_collection->IsForSizing())
     return;
 
-  auto* sizing_collection = To<NGGridSizingTrackCollection>(track_collection);
-  const auto track_direction = sizing_collection->Direction();
+  auto& sizing_collection = To<NGGridSizingTrackCollection>(*track_collection);
+  const auto track_direction = sizing_collection.Direction();
   const bool is_for_columns = track_direction == kForColumns;
 
   LayoutUnit available_size = is_for_columns ? grid_available_size_.inline_size
                                              : grid_available_size_.block_size;
 
-  for (auto set_iterator = sizing_collection->GetSetIterator();
-       !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
-    auto& current_set = set_iterator.CurrentSet();
-    const auto& track_size = current_set.track_size;
-    DCHECK_NE(track_size.GetType(), kLengthTrackSizing);
+  auto& track_properties = is_for_columns ? grid_properties->column_properties
+                                          : grid_properties->row_properties;
 
-    if (track_size.IsFitContent()) {
-      // Indefinite lengths cannot occur, as they must be normalized to 'auto'.
-      DCHECK(!track_size.FitContentTrackBreadth().HasPercentage() ||
-             available_size != kIndefiniteSize);
-
-      LayoutUnit fit_content_argument = MinimumValueForLength(
-          track_size.FitContentTrackBreadth().length(), available_size);
-      current_set.fit_content_limit =
-          fit_content_argument * current_set.track_count;
-    }
-
-    if (track_size.HasFixedMaxTrackBreadth()) {
-      DCHECK(!track_size.MaxTrackBreadth().HasPercentage() ||
-             available_size != kIndefiniteSize);
-
-      // A fixed sizing function: Resolve to an absolute length and use that
-      // size as the track’s initial growth limit; if the growth limit is less
-      // than the base size, increase the growth limit to match the base size.
-      LayoutUnit fixed_max_breadth = MinimumValueForLength(
-          track_size.MaxTrackBreadth().length(), available_size);
-      current_set.growth_limit = fixed_max_breadth * current_set.track_count;
-    } else {
-      // An intrinsic or flexible sizing function: Use an initial growth limit
-      // of infinity.
-      current_set.growth_limit = kIndefiniteSize;
-    }
-
-    if (track_size.HasFixedMinTrackBreadth()) {
-      DCHECK(!track_size.MinTrackBreadth().HasPercentage() ||
-             available_size != kIndefiniteSize);
-
-      // A fixed sizing function: Resolve to an absolute length and use that
-      // size as the track’s initial base size.
-      LayoutUnit fixed_min_breadth = MinimumValueForLength(
-          track_size.MinTrackBreadth().length(), available_size);
-      current_set.InitBaseSize(fixed_min_breadth * current_set.track_count);
-    } else {
-      // An intrinsic sizing function: Use an initial base size of zero.
-      DCHECK(track_size.HasIntrinsicMinTrackBreadth());
-      current_set.InitBaseSize(LayoutUnit());
-    }
-  }
+  track_properties = sizing_collection.InitializeSets(Style(), available_size);
 
   LayoutUnit start_border_scrollbar_padding =
       is_for_columns ? BorderScrollbarPadding().inline_start
@@ -1627,16 +1556,16 @@
 
   // If all of our tracks have a definite size upfront, we can use the current
   // set sizes as the used track sizes (applying alignment, if present).
-  if (grid_properties.IsSpanningOnlyDefiniteTracks(track_direction)) {
-    sizing_collection->SetGutterSize(GutterSize(track_direction));
+  if (grid_properties->IsSpanningOnlyDefiniteTracks(track_direction)) {
+    sizing_collection.SetGutterSize(GutterSize(track_direction));
     auto first_set_geometry =
-        ComputeFirstSetGeometry(*sizing_collection, Style(), available_size,
+        ComputeFirstSetGeometry(sizing_collection, Style(), available_size,
                                 start_border_scrollbar_padding);
-    sizing_collection->CacheSetsGeometry(first_set_geometry.start_offset,
-                                         first_set_geometry.gutter_size);
+    sizing_collection.CacheSetsGeometry(first_set_geometry.start_offset,
+                                        first_set_geometry.gutter_size);
   } else {
-    sizing_collection->InitializeSetsGeometry(start_border_scrollbar_padding,
-                                              GutterSize(track_direction));
+    sizing_collection.InitializeSetsGeometry(start_border_scrollbar_padding,
+                                             GutterSize(track_direction));
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
index 5729b4341..2ff0b13 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
@@ -61,8 +61,8 @@
  private:
   friend class NGGridLayoutAlgorithmTest;
 
-  // Aggregate all direct OOF children from the current grid to `oof_children`,
-  // unless `oof_children` is nullptr.
+  // Aggregate all direct out of flow children from the current grid container
+  // to |oof_children|, unless |oof_children| is not provided.
   wtf_size_t BuildGridSizingSubtree(
       NGGridSizingTree* sizing_tree,
       HeapVector<Member<LayoutBox>>* oof_children = nullptr,
@@ -79,9 +79,7 @@
                       const GridItemData& grid_item,
                       const GridTrackSizingDirection track_direction) const;
 
-  void ComputeGridGeometry(const NGGridPlacementData& placement_data,
-                           GridItems* grid_items,
-                           NGGridLayoutData* layout_data,
+  void ComputeGridGeometry(NGGridSizingTree* grid_sizing_tree,
                            LayoutUnit* intrinsic_block_size);
 
   LayoutUnit ComputeIntrinsicBlockSizeIgnoringChildren() const;
@@ -111,9 +109,9 @@
   wtf_size_t ComputeAutomaticRepetitions(
       const GridTrackSizingDirection track_direction) const;
 
-  void BuildBlockTrackCollection(
-      GridItems* grid_items,
-      NGGridBlockTrackCollection* track_collection) const;
+  void BuildBlockTrackCollection(GridTrackSizingDirection track_direction,
+                                 GridItems* grid_items,
+                                 NGGridRangeBuilder* track_collection) const;
 
   // Determines the major/minor alignment baselines for each row/column based on
   // each item in |grid_items|, and stores the results in |track_collection|.
@@ -125,9 +123,8 @@
       bool* needs_additional_pass = nullptr) const;
 
   // Initializes the given track collection, and returns the base set geometry.
-  void InitializeTrackSizes(
-      const NGGridProperties& grid_properties,
-      NGGridLayoutTrackCollection* track_collection) const;
+  void InitializeTrackSizes(NGGridLayoutTrackCollection* track_collection,
+                            NGGridProperties* grid_properties) const;
 
   // Calculates from the min and max track sizing functions the used track size.
   void ComputeUsedTrackSizes(const NGGridLayoutData& layout_data,
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
index f2332e2..16e69f38 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
@@ -39,18 +39,13 @@
   void SetUp() override { NGBaseLayoutAlgorithmTest::SetUp(); }
 
   void BuildGridItemsAndTrackCollections(NGGridLayoutAlgorithm& algorithm) {
-    const auto& node = algorithm.Node();
-
-    bool has_nested_subgrid;
-    auto grid_items = node.ConstructGridItems(algorithm.PlacementData(),
-                                              /* oof_children */ nullptr,
-                                              &has_nested_subgrid);
-
     LayoutUnit unused_intrinsic_block_size;
-    algorithm.ComputeGridGeometry(node.CachedPlacementData(), &grid_items,
-                                  &layout_data_, &unused_intrinsic_block_size);
+    auto grid_sizing_tree = algorithm.BuildGridSizingTree();
+    algorithm.ComputeGridGeometry(&grid_sizing_tree,
+                                  &unused_intrinsic_block_size);
 
-    *cached_grid_items_ = grid_items.item_data;
+    *cached_grid_items_ = std::move(grid_sizing_tree[0].grid_items.item_data);
+    layout_data_ = std::move(grid_sizing_tree[0].layout_data);
   }
 
   const GridItemData& GridItem(wtf_size_t index) {
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h
index ec0ce1a3..10b14b74 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.h
@@ -21,7 +21,7 @@
 
   const NGGridPlacementData& CachedPlacementData() const;
 
-  // Aggregate any OOF children to `oof_children`, unless nullptr.
+  // If |oof_children| is provided, aggregate any out of flow children.
   GridItems ConstructGridItems(const NGGridPlacementData& placement_data,
                                HeapVector<Member<LayoutBox>>* oof_children,
                                bool* has_nested_subgrid) const;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.cc
index 21fff36..d9a1d5d 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.cc
@@ -51,6 +51,14 @@
                    TrackSpanProperties::kHasNonDefiniteTrack);
 }
 
+bool NGGridSizingData::MustBuildLayoutData(
+    GridTrackSizingDirection track_direction) const {
+  return !subgrid_data_in_parent ||
+         ((track_direction == kForColumns)
+              ? !subgrid_data_in_parent->has_subgridded_columns
+              : !subgrid_data_in_parent->has_subgridded_rows);
+}
+
 NGGridSizingData& NGGridSizingTree::CreateSizingData(
     const NGGridNode& grid,
     const NGGridSizingData* parent_sizing_data,
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.h
index 0ed62337..af86f4e 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_sizing_tree.h
@@ -51,6 +51,8 @@
       : parent_sizing_data(parent_sizing_data),
         subgrid_data_in_parent(subgrid_data_in_parent) {}
 
+  bool MustBuildLayoutData(GridTrackSizingDirection track_direction) const;
+
   void Trace(Visitor* visitor) const {
     visitor->Trace(grid_items);
     visitor->Trace(parent_sizing_data);
@@ -61,15 +63,12 @@
   NGGridProperties grid_properties;
   NGGridLayoutData layout_data;
 
-  std::unique_ptr<NGGridBlockTrackCollection> column_builder_collection;
-  std::unique_ptr<NGGridBlockTrackCollection> row_builder_collection;
-
   Member<const NGGridSizingData> parent_sizing_data;
   Member<const GridItemData> subgrid_data_in_parent;
   wtf_size_t subtree_size{1};
 };
 
-class NGGridSizingTree {
+class CORE_EXPORT NGGridSizingTree {
   STACK_ALLOCATED();
 
  public:
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
index 6d5e983c..ae62232 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
@@ -10,13 +10,12 @@
 
 namespace blink {
 
-struct SameSizeAsNGGridLayoutTrackCollectionRange {
-  wtf_size_t members[4];
+struct SameSizeAsNGGridRange {
+  wtf_size_t members[6];
   wtf_size_t bitfields;
 };
 
-ASSERT_SIZE(NGGridLayoutTrackCollection::Range,
-            SameSizeAsNGGridLayoutTrackCollectionRange);
+ASSERT_SIZE(NGGridRange, SameSizeAsNGGridRange);
 
 wtf_size_t NGGridTrackCollectionBase::RangeEndLine(
     wtf_size_t range_index) const {
@@ -48,34 +47,33 @@
   return lower;
 }
 
-bool NGGridBlockTrackCollection::Range::IsCollapsed() const {
+bool NGGridRange::IsCollapsed() const {
   return properties.HasProperty(TrackSpanProperties::kIsCollapsed);
 }
 
-bool NGGridBlockTrackCollection::Range::IsImplicit() const {
+bool NGGridRange::IsImplicit() const {
   return properties.HasProperty(TrackSpanProperties::kIsImplicit);
 }
 
-void NGGridBlockTrackCollection::Range::SetIsCollapsed() {
+void NGGridRange::SetIsCollapsed() {
   properties.SetProperty(TrackSpanProperties::kIsCollapsed);
 }
 
-void NGGridBlockTrackCollection::Range::SetIsImplicit() {
+void NGGridRange::SetIsImplicit() {
   properties.SetProperty(TrackSpanProperties::kIsImplicit);
 }
 
-NGGridBlockTrackCollection::NGGridBlockTrackCollection(
+NGGridRangeBuilder::NGGridRangeBuilder(
     const ComputedStyle& grid_style,
     const NGGridPlacementData& placement_data,
     GridTrackSizingDirection track_direction)
-    : NGGridTrackCollectionBase(track_direction),
-      auto_repetitions_((track_direction == kForColumns)
+    : auto_repetitions_((track_direction == kForColumns)
                             ? placement_data.column_auto_repetitions
                             : placement_data.row_auto_repetitions),
       start_offset_((track_direction == kForColumns)
                         ? placement_data.column_start_offset
                         : placement_data.row_start_offset),
-      track_indices_need_sort_(false),
+      must_sort_grid_lines(false),
       explicit_tracks_((track_direction == kForColumns)
                            ? grid_style.GridTemplateColumns().TrackList()
                            : grid_style.GridTemplateRows().TrackList()),
@@ -84,13 +82,13 @@
                            : grid_style.GridAutoRows().NGTrackList()) {
   // The implicit track list should have only one repeater, if any.
   DCHECK_LE(implicit_tracks_.RepeaterCount(), 1u);
-  DCHECK_NE(kNotFound, auto_repetitions_);
+  DCHECK_NE(auto_repetitions_, kNotFound);
 
   const wtf_size_t repeater_count = explicit_tracks_.RepeaterCount();
 
   // Add extra capacity for the extra lines needed for named grids.
-  start_lines_.reserve(repeater_count + 1);
-  end_lines_.reserve(repeater_count + 1);
+  start_lines_.ReserveInitialCapacity(repeater_count + 1);
+  end_lines_.ReserveInitialCapacity(repeater_count + 1);
 
   wtf_size_t current_repeater_start_line = start_offset_;
   for (wtf_size_t i = 0; i < repeater_count; ++i) {
@@ -123,39 +121,45 @@
   }
 }
 
-void NGGridBlockTrackCollection::EnsureTrackCoverage(
+void NGGridRangeBuilder::EnsureTrackCoverage(
     wtf_size_t start_line,
     wtf_size_t span_length,
     wtf_size_t* grid_item_start_range_index,
     wtf_size_t* grid_item_end_range_index) {
-  DCHECK_NE(kNotFound, start_line);
-  DCHECK_NE(kNotFound, span_length);
+  DCHECK_NE(start_line, kNotFound);
+  DCHECK_NE(span_length, kNotFound);
   DCHECK(grid_item_start_range_index && grid_item_end_range_index);
 
-  track_indices_need_sort_ = true;
+  must_sort_grid_lines = true;
   start_lines_.emplace_back(start_line, grid_item_start_range_index);
   end_lines_.emplace_back(start_line + span_length, grid_item_end_range_index);
 }
 
-void NGGridBlockTrackCollection::FinalizeRanges() {
-  DCHECK(ranges_.empty());
+NGGridRangeVector NGGridRangeBuilder::FinalizeRanges() {
+  DCHECK_LE(start_lines_.size(), end_lines_.size());
 
   // Sort start and ending tracks from low to high.
-  if (track_indices_need_sort_) {
+  if (must_sort_grid_lines) {
     auto CompareTrackBoundaries = [](const TrackBoundaryToRangePair& a,
                                      const TrackBoundaryToRangePair& b) {
       return a.grid_line < b.grid_line;
     };
     std::sort(start_lines_.begin(), start_lines_.end(), CompareTrackBoundaries);
     std::sort(end_lines_.begin(), end_lines_.end(), CompareTrackBoundaries);
+    must_sort_grid_lines = false;
   }
 
+  const wtf_size_t explicit_repeater_count = explicit_tracks_.RepeaterCount();
+  const wtf_size_t grid_line_count = start_lines_.size();
+
+  NGGridRangeVector ranges;
   bool is_in_auto_fit_range = false;
-  wtf_size_t current_range_start_line = 0u;
-  wtf_size_t open_items_or_repeaters = 0u;
+
   wtf_size_t current_explicit_grid_line = start_offset_;
   wtf_size_t current_explicit_repeater_index = kNotFound;
-  wtf_size_t explicit_repeater_count = explicit_tracks_.RepeaterCount();
+  wtf_size_t current_range_start_line = 0;
+  wtf_size_t current_set_index = 0;
+  wtf_size_t open_items_or_repeaters = 0;
 
   // If the explicit grid is not empty, |start_offset_| is the translated index
   // of the first track in |explicit_tracks_|; otherwise, the next repeater
@@ -164,12 +168,12 @@
       explicit_repeater_count ? start_offset_ : kNotFound;
 
   // Index of the start/end line we are currently processing.
-  wtf_size_t start_line_index = 0u;
-  wtf_size_t end_line_index = 0u;
+  wtf_size_t start_line_index = 0;
+  wtf_size_t end_line_index = 0;
 
   while (true) {
     // Identify starting tracks index.
-    while (start_line_index < start_lines_.size() &&
+    while (start_line_index < grid_line_count &&
            current_range_start_line >=
                start_lines_[start_line_index].grid_line) {
       ++start_line_index;
@@ -177,32 +181,23 @@
     }
 
     // Identify ending tracks index.
-    while (end_line_index < end_lines_.size() &&
+    while (end_line_index < grid_line_count &&
            current_range_start_line >= end_lines_[end_line_index].grid_line) {
       ++end_line_index;
       --open_items_or_repeaters;
       DCHECK_GE(open_items_or_repeaters, 0u);
     }
 
-    // Identify ending tracks index.
-    if (end_line_index >= end_lines_.size()) {
-#if DCHECK_IS_ON()
-      DCHECK_EQ(open_items_or_repeaters, 0u);
-      // If we exhausted the end indices, then we must have already exhausted
-      // the repeaters, or are located at the end of the last repeater.
-      if (current_explicit_repeater_index != kNotFound) {
-        DCHECK_EQ(current_explicit_repeater_index, explicit_repeater_count - 1);
-        DCHECK_EQ(current_range_start_line, next_explicit_repeater_start);
-      }
-#endif
+    if (end_line_index >= grid_line_count)
       break;
-    }
 
     // Determine the next starting and ending track index.
-    wtf_size_t next_start_line = (start_line_index < start_lines_.size())
-                                     ? start_lines_[start_line_index].grid_line
-                                     : kNotFound;
-    wtf_size_t next_end_line = end_lines_[end_line_index].grid_line;
+    const wtf_size_t next_start_line =
+        (start_line_index < grid_line_count)
+            ? start_lines_[start_line_index].grid_line
+            : kNotFound;
+    const wtf_size_t next_end_line = end_lines_[end_line_index].grid_line;
+    DCHECK(next_start_line != kNotFound || next_end_line < next_start_line);
 
     // Move to the start of the next explicit repeater.
     while (current_range_start_line == next_explicit_repeater_start) {
@@ -224,30 +219,36 @@
                                        auto_repetitions_);
     }
 
-    // Determine track number and count of the range.
-    Range range;
+    // Compute this range's begin set index, start line, and track count.
+    NGGridRange range;
+    wtf_size_t current_repeater_size = 1;
     range.start_line = current_range_start_line;
-    DCHECK(next_start_line != kNotFound || next_end_line < next_start_line);
     range.track_count =
         std::min(next_start_line, next_end_line) - current_range_start_line;
     DCHECK_GT(range.track_count, 0u);
 
-    // Compute repeater index and offset.
+    // Compute current repeater's index, size, and offset.
+    range.begin_set_index = current_set_index;
     if (current_explicit_repeater_index != kNotFound) {
+      current_repeater_size =
+          explicit_tracks_.RepeatSize(current_explicit_repeater_index);
+
       // This range is contained within a repeater of the explicit grid; at this
       // point, |current_explicit_grid_line| should be set to the start line of
       // such repeater.
       range.repeater_index = current_explicit_repeater_index;
       range.repeater_offset =
           (current_range_start_line - current_explicit_grid_line) %
-          explicit_tracks_.RepeatSize(current_explicit_repeater_index);
+          current_repeater_size;
     } else {
       range.SetIsImplicit();
-      if (implicit_tracks_.RepeaterCount() == 0u) {
+      if (!implicit_tracks_.RepeaterCount()) {
         // No specified implicit grid tracks, use 'auto'.
         range.repeater_index = kNotFound;
         range.repeater_offset = 0u;
       } else {
+        current_repeater_size = implicit_tracks_.RepeatSize(0);
+
         // Otherwise, use the only repeater for implicit grid tracks.
         // There are 2 scenarios we want to cover here:
         //   1. At this point, we should not have reached any explicit repeater,
@@ -266,15 +267,14 @@
         //
         // Note that for both scenarios we can use the following formula:
         //   (current_range_start_line - current_explicit_grid_line) %
-        //   implicit_repeater_size
+        //   current_repeater_size
         // The expression below is equivalent, but uses some modular arithmetic
         // properties to avoid |wtf_size_t| underflow in scenario 1.
-        range.repeater_index = 0u;
-        wtf_size_t implicit_repeater_size = implicit_tracks_.RepeatSize(0u);
+        range.repeater_index = 0;
         range.repeater_offset =
-            (current_range_start_line + implicit_repeater_size -
-             current_explicit_grid_line % implicit_repeater_size) %
-            implicit_repeater_size;
+            (current_range_start_line + current_repeater_size -
+             current_explicit_grid_line % current_repeater_size) %
+            current_repeater_size;
       }
     }
 
@@ -284,13 +284,13 @@
     // Walk back to cache all duplicates until we are at the start of the vector
     // or we have gone over all duplicate entries.
     if (start_line_index != 0) {
-      DCHECK_LE(start_line_index, start_lines_.size());
+      DCHECK_LE(start_line_index, grid_line_count);
       for (wtf_size_t line_index = start_line_index - 1;
            start_lines_[line_index].grid_line == range.start_line;
            --line_index) {
         if (start_lines_[line_index].grid_item_range_index_to_cache) {
           *start_lines_[line_index].grid_item_range_index_to_cache =
-              ranges_.size();
+              ranges.size();
         }
         // This is needed here to avoid underflow.
         if (!line_index)
@@ -304,32 +304,51 @@
     // of the vector or we have gone over all duplicate entries.
     const wtf_size_t end_line = range.start_line + range.track_count;
     for (wtf_size_t line_index = end_line_index;
-         line_index < end_lines_.size() &&
+         line_index < grid_line_count &&
          end_lines_[line_index].grid_line == end_line;
          ++line_index) {
       if (end_lines_[line_index].grid_item_range_index_to_cache)
-        *end_lines_[line_index].grid_item_range_index_to_cache = ranges_.size();
+        *end_lines_[line_index].grid_item_range_index_to_cache = ranges.size();
     }
 
-    if (is_in_auto_fit_range && open_items_or_repeaters == 1u)
+    if (is_in_auto_fit_range && open_items_or_repeaters == 1) {
       range.SetIsCollapsed();
+      range.set_count = 0;
+    } else {
+      // If this is a non-collapsed range, the number of sets in this range is
+      // the number of track definitions in the current repeater clamped by the
+      // track count if it's less than the repeater's size.
+      range.set_count = std::min(current_repeater_size, range.track_count);
+      DCHECK_GT(range.set_count, 0u);
+    }
+
     current_range_start_line += range.track_count;
-    ranges_.emplace_back(std::move(range));
+    current_set_index += range.set_count;
+    ranges.emplace_back(std::move(range));
   }
 
+#if DCHECK_IS_ON()
   // We must have exhausted all start and end indices.
-  DCHECK_EQ(start_line_index, start_lines_.size());
-  DCHECK_EQ(end_line_index, end_lines_.size());
+  DCHECK_EQ(start_line_index, grid_line_count);
+  DCHECK_EQ(end_line_index, grid_line_count);
+  DCHECK_EQ(open_items_or_repeaters, 0u);
+
+  // If we exhausted the end indices, then we must have already exhausted the
+  // repeaters, or are located at the end of the last repeater.
+  if (current_explicit_repeater_index != kNotFound) {
+    DCHECK_EQ(current_explicit_repeater_index, explicit_repeater_count - 1);
+    DCHECK_EQ(current_range_start_line, next_explicit_repeater_start);
+  }
+#endif
+  return ranges;
 }
 
-NGGridBlockTrackCollection::NGGridBlockTrackCollection(
-    const NGGridTrackList& explicit_tracks,
-    const NGGridTrackList& implicit_tracks,
-    wtf_size_t auto_repetitions)
-    : NGGridTrackCollectionBase(kForColumns),
-      auto_repetitions_(auto_repetitions),
+NGGridRangeBuilder::NGGridRangeBuilder(const NGGridTrackList& explicit_tracks,
+                                       const NGGridTrackList& implicit_tracks,
+                                       wtf_size_t auto_repetitions)
+    : auto_repetitions_(auto_repetitions),
       start_offset_(0),
-      track_indices_need_sort_(false),
+      must_sort_grid_lines(false),
       explicit_tracks_(explicit_tracks),
       implicit_tracks_(implicit_tracks) {
   const wtf_size_t repeater_count = explicit_tracks_.RepeaterCount();
@@ -347,18 +366,6 @@
   }
 }
 
-wtf_size_t NGGridBlockTrackCollection::RangeStartLine(
-    wtf_size_t range_index) const {
-  DCHECK_LT(range_index, ranges_.size());
-  return ranges_[range_index].start_line;
-}
-
-wtf_size_t NGGridBlockTrackCollection::RangeTrackCount(
-    wtf_size_t range_index) const {
-  DCHECK_LT(range_index, ranges_.size());
-  return ranges_[range_index].track_count;
-}
-
 NGGridSet::NGGridSet(wtf_size_t track_count)
     : track_count(track_count),
       track_size(Length::Auto(), Length::Auto()),
@@ -456,16 +463,12 @@
   return growth_limit != kIndefiniteSize && growth_limit < base_size;
 }
 
-bool NGGridLayoutTrackCollection::Range::IsCollapsed() const {
-  return properties.HasProperty(TrackSpanProperties::kIsCollapsed);
-}
-
 NGGridLayoutTrackCollection::NGGridLayoutTrackCollection(
     const NGGridLayoutTrackCollection& other,
     const NGBoxStrut& subgrid_border_scrollbar_padding,
     const NGBoxStrut& subgrid_margins)
     : NGGridLayoutTrackCollection(other) {
-  const bool is_for_columns = Direction() == kForColumns;
+  const bool is_for_columns = track_direction_ == kForColumns;
 
   sets_geometry_start_offset_ += is_for_columns ? subgrid_margins.inline_start
                                                 : subgrid_margins.block_start;
@@ -517,11 +520,10 @@
   return ranges_[range_index].begin_set_index;
 }
 
-bool NGGridLayoutTrackCollection::RangeHasTrackSpanProperty(
-    wtf_size_t range_index,
-    TrackSpanProperties::PropertyId property_id) const {
+TrackSpanProperties NGGridLayoutTrackCollection::RangeProperties(
+    wtf_size_t range_index) const {
   DCHECK_LT(range_index, ranges_.size());
-  return ranges_[range_index].properties.HasProperty(property_id);
+  return ranges_[range_index].properties;
 }
 
 wtf_size_t NGGridLayoutTrackCollection::EndLineOfImplicitGrid() const {
@@ -567,6 +569,11 @@
   return sets_geometry_[set_index + 1].track_count;
 }
 
+bool NGGridLayoutTrackCollection::HasBaselines() const {
+  DCHECK_EQ(major_baselines_.size(), minor_baselines_.size());
+  return !major_baselines_.empty();
+}
+
 LayoutUnit NGGridLayoutTrackCollection::MajorBaseline(
     wtf_size_t set_index) const {
   DCHECK_LT(set_index, major_baselines_.size());
@@ -626,7 +633,7 @@
   const wtf_size_t begin_set_index = ranges_[begin_range_index].begin_set_index;
 
   for (wtf_size_t i = begin_range_index; i <= end_range_index; ++i) {
-    Range translated_range = ranges_[i];
+    NGGridRange translated_range = ranges_[i];
     translated_range.start_line -= start_line_offset;
     translated_range.begin_set_index -= begin_set_index;
     subgrid_collection.ranges_.emplace_back(std::move(translated_range));
@@ -673,21 +680,23 @@
 }
 
 NGGridSizingTrackCollection::NGGridSizingTrackCollection(
-    const NGGridBlockTrackCollection& block_track_collection,
-    bool is_available_size_indefinite)
-    : NGGridLayoutTrackCollection(block_track_collection.Direction()),
+    NGGridRangeVector&& ranges,
+    GridTrackSizingDirection track_direction)
+    : NGGridLayoutTrackCollection(track_direction),
       non_collapsed_track_count_(0) {
-  for (const auto& block_track_range : block_track_collection.Ranges()) {
-    AppendTrackRange(block_track_range,
-                     block_track_range.IsImplicit()
-                         ? block_track_collection.ImplicitTracks()
-                         : block_track_collection.ExplicitTracks(),
-                     is_available_size_indefinite);
+  ranges_ = std::move(ranges);
+
+  wtf_size_t set_count = 0;
+  for (const auto& range : ranges_) {
+    if (!range.IsCollapsed()) {
+      non_collapsed_track_count_ += range.track_count;
+      set_count += range.set_count;
+    }
   }
 
-  const wtf_size_t set_count = sets_.size() + 1;
-  last_indefinite_indices_.ReserveInitialCapacity(set_count);
-  sets_geometry_.ReserveInitialCapacity(set_count);
+  last_indefinite_indices_.ReserveInitialCapacity(set_count + 1);
+  sets_geometry_.ReserveInitialCapacity(set_count + 1);
+  sets_.ReserveInitialCapacity(set_count);
 }
 
 NGGridSet& NGGridSizingTrackCollection::GetSetAt(wtf_size_t set_index) {
@@ -788,8 +797,8 @@
 
 void NGGridSizingTrackCollection::ResetBaselines() {
   const wtf_size_t set_count = sets_.size();
-  major_baselines_ = Vector<LayoutUnit>(set_count, LayoutUnit::Min());
-  minor_baselines_ = Vector<LayoutUnit>(set_count, LayoutUnit::Min());
+  major_baselines_ = Vector<LayoutUnit, 16>(set_count, LayoutUnit::Min());
+  minor_baselines_ = Vector<LayoutUnit, 16>(set_count, LayoutUnit::Min());
 }
 
 void NGGridSizingTrackCollection::SetMajorBaseline(
@@ -808,42 +817,142 @@
     minor_baselines_[set_index] = candidate_baseline;
 }
 
-void NGGridSizingTrackCollection::AppendTrackRange(
-    const NGGridBlockTrackCollection::Range& block_track_range,
+TrackSpanProperties NGGridSizingTrackCollection::InitializeSets(
+    const ComputedStyle& grid_style,
+    LayoutUnit grid_available_size) {
+  const bool is_for_columns = track_direction_ == kForColumns;
+
+  return InitializeSets(
+      is_for_columns ? grid_style.GridTemplateColumns().TrackList()
+                     : grid_style.GridTemplateRows().TrackList(),
+      is_for_columns ? grid_style.GridAutoColumns().NGTrackList()
+                     : grid_style.GridAutoRows().NGTrackList(),
+      grid_available_size);
+}
+
+TrackSpanProperties NGGridSizingTrackCollection::InitializeSets(
+    const NGGridTrackList& explicit_track_list,
+    const NGGridTrackList& implicit_track_list,
+    LayoutUnit grid_available_size) {
+  TrackSpanProperties collection_properties;
+
+  sets_.Shrink(0);
+  {
+    const bool is_available_size_indefinite =
+        grid_available_size == kIndefiniteSize;
+
+    for (auto& range : ranges_) {
+      AppendSetsForRange(
+          range.IsImplicit() ? implicit_track_list : explicit_track_list,
+          is_available_size_indefinite, &range);
+      collection_properties |= range.properties;
+    }
+  }
+
+  for (auto& set : sets_) {
+    const auto& track_size = set.track_size;
+    DCHECK_NE(track_size.GetType(), kLengthTrackSizing);
+
+    if (track_size.IsFitContent()) {
+      // Indefinite lengths cannot occur, as they must be normalized to 'auto'.
+      DCHECK(!track_size.FitContentTrackBreadth().HasPercentage() ||
+             grid_available_size != kIndefiniteSize);
+
+      LayoutUnit fit_content_argument = MinimumValueForLength(
+          track_size.FitContentTrackBreadth().length(), grid_available_size);
+      set.fit_content_limit = fit_content_argument * set.track_count;
+    }
+
+    if (track_size.HasFixedMaxTrackBreadth()) {
+      DCHECK(!track_size.MaxTrackBreadth().HasPercentage() ||
+             grid_available_size != kIndefiniteSize);
+
+      // A fixed sizing function: Resolve to an absolute length and use that
+      // size as the track’s initial growth limit; if the growth limit is less
+      // than the base size, increase the growth limit to match the base size.
+      LayoutUnit fixed_max_breadth = MinimumValueForLength(
+          track_size.MaxTrackBreadth().length(), grid_available_size);
+      set.growth_limit = fixed_max_breadth * set.track_count;
+    } else {
+      // An intrinsic or flexible sizing function: Use an initial growth limit
+      // of infinity.
+      set.growth_limit = kIndefiniteSize;
+    }
+
+    if (track_size.HasFixedMinTrackBreadth()) {
+      DCHECK(!track_size.MinTrackBreadth().HasPercentage() ||
+             grid_available_size != kIndefiniteSize);
+
+      // A fixed sizing function: Resolve to an absolute length and use that
+      // size as the track’s initial base size.
+      LayoutUnit fixed_min_breadth = MinimumValueForLength(
+          track_size.MinTrackBreadth().length(), grid_available_size);
+      set.InitBaseSize(fixed_min_breadth * set.track_count);
+    } else {
+      // An intrinsic sizing function: Use an initial base size of zero.
+      DCHECK(track_size.HasIntrinsicMinTrackBreadth());
+      set.InitBaseSize(LayoutUnit());
+    }
+  }
+  return collection_properties;
+}
+
+void NGGridSizingTrackCollection::AppendSetsForRange(
     const NGGridTrackList& specified_track_list,
-    bool is_available_size_indefinite) {
-  Range new_range;
+    bool is_available_size_indefinite,
+    NGGridRange* range) {
+  // Notice that |NGGridRange::Reset| does not reset the |kIsCollapsed| or
+  // |kIsImplicit| flags as they're not affected by the set definitions.
+  range->properties.Reset();
 
-  new_range.begin_set_index = sets_.size();
-  new_range.properties = block_track_range.properties;
-  new_range.start_line = block_track_range.start_line;
-  new_range.track_count = block_track_range.track_count;
+  auto CacheLastAppendedSetProperties = [&]() {
+    DCHECK(!sets_.empty());
+    const auto& set_track_size = sets_.back().track_size;
 
-  if (block_track_range.repeater_index == kNotFound) {
+    // From https://drafts.csswg.org/css-grid-2/#algo-terms, a <flex> minimum
+    // sizing function shouldn't happen as it would be normalized to 'auto'.
+    DCHECK(!set_track_size.HasFlexMinTrackBreadth());
+
+    if (set_track_size.HasAutoMinTrackBreadth())
+      range->properties.SetProperty(TrackSpanProperties::kHasAutoMinimumTrack);
+
+    if (set_track_size.HasFixedMinTrackBreadth())
+      range->properties.SetProperty(TrackSpanProperties::kHasFixedMinimumTrack);
+
+    if (set_track_size.HasFixedMaxTrackBreadth())
+      range->properties.SetProperty(TrackSpanProperties::kHasFixedMaximumTrack);
+
+    if (set_track_size.HasFlexMaxTrackBreadth()) {
+      range->properties.SetProperty(TrackSpanProperties::kHasFlexibleTrack);
+      range->properties.SetProperty(
+          TrackSpanProperties::kIsDependentOnAvailableSize);
+    }
+
+    if (set_track_size.HasIntrinsicMinTrackBreadth() ||
+        set_track_size.HasIntrinsicMaxTrackBreadth()) {
+      range->properties.SetProperty(TrackSpanProperties::kHasIntrinsicTrack);
+    }
+
+    if (!set_track_size.HasFixedMinTrackBreadth() ||
+        !set_track_size.HasFixedMaxTrackBreadth() ||
+        (set_track_size.MinTrackBreadth().length() !=
+         set_track_size.MaxTrackBreadth().length())) {
+      range->properties.SetProperty(TrackSpanProperties::kHasNonDefiniteTrack);
+    }
+  };
+
+  if (range->repeater_index == kNotFound) {
     // The only case where a range doesn't have a repeater index is when the
     // range is in the implicit grid and there are no auto track definitions;
     // fill the entire range with a single set of 'auto' tracks.
-    DCHECK(block_track_range.IsImplicit());
+    DCHECK(range->IsImplicit());
 
-    non_collapsed_track_count_ += new_range.track_count;
-    new_range.set_count = 1;
-    sets_.emplace_back(new_range.track_count);
-  } else if (block_track_range.IsCollapsed()) {
-    // Append a range that contains the collapsed tracks, but do not append new
-    // sets so that its tracks do not participate in the track sizing algorithm.
-    new_range.set_count = 0;
-  } else {
-    non_collapsed_track_count_ += new_range.track_count;
-    wtf_size_t current_repeater_size =
-        specified_track_list.RepeatSize(block_track_range.repeater_index);
-    DCHECK_LT(block_track_range.repeater_offset, current_repeater_size);
-
-    // The number of different set elements in this range is the number of track
-    // definitions from |NGGridBlockTrackCollection| range's repeater clamped by
-    // the range's total track count if it's less than the repeater's size.
-    new_range.set_count =
-        std::min(current_repeater_size, new_range.track_count);
-    DCHECK_GT(new_range.set_count, 0u);
+    sets_.emplace_back(range->track_count);
+    CacheLastAppendedSetProperties();
+  } else if (!range->IsCollapsed()) {
+    const wtf_size_t current_repeater_size =
+        specified_track_list.RepeatSize(range->repeater_index);
+    DCHECK_LT(range->repeater_offset, current_repeater_size);
 
     // The following two variables help compute how many tracks a set element
     // compresses; suppose we want to print this range, we would circle through
@@ -853,74 +962,36 @@
     // 1. |floor_set_track_count| is the number of times we would return to the
     // range's repeater offset, meaning that every definition in the repeater's
     // track list appears at least that many times within the range.
-    wtf_size_t floor_set_track_count =
-        new_range.track_count / current_repeater_size;
+    const wtf_size_t floor_set_track_count =
+        range->track_count / current_repeater_size;
+
     // 2. The remaining track count would not complete another iteration over
     // the entire repeater; this means that the first |remaining_track_count|
     // definitions appear one more time in the range.
-    wtf_size_t remaining_track_count =
-        new_range.track_count % current_repeater_size;
+    const wtf_size_t remaining_track_count =
+        range->track_count % current_repeater_size;
 
-    for (wtf_size_t i = 0; i < new_range.set_count; ++i) {
-      wtf_size_t set_track_count =
+    for (wtf_size_t i = 0; i < range->set_count; ++i) {
+      const wtf_size_t set_track_count =
           floor_set_track_count + ((i < remaining_track_count) ? 1 : 0);
-      wtf_size_t set_repeater_offset =
-          (block_track_range.repeater_offset + i) % current_repeater_size;
-      const GridTrackSize& set_track_size =
-          specified_track_list.RepeatTrackSize(block_track_range.repeater_index,
-                                               set_repeater_offset);
-      sets_.emplace_back(set_track_count, set_track_size,
-                         is_available_size_indefinite);
+      const wtf_size_t set_repeater_offset =
+          (range->repeater_offset + i) % current_repeater_size;
+      const auto& set_track_size = specified_track_list.RepeatTrackSize(
+          range->repeater_index, set_repeater_offset);
 
-      // Record if any of the tracks depend on the available-size. We need to
-      // record any percentage tracks *before* normalization as they will
-      // change once the available-size becomes definite.
+      // Record if any of the track sizes depend on the available size; we need
+      // to record any percentage tracks *before* normalization as they will
+      // change to 'auto' if the available size is indefinite.
       if (set_track_size.HasPercentage()) {
-        new_range.properties.SetProperty(
+        range->properties.SetProperty(
             TrackSpanProperties::kIsDependentOnAvailableSize);
       }
+
+      sets_.emplace_back(set_track_count, set_track_size,
+                         is_available_size_indefinite);
+      CacheLastAppendedSetProperties();
     }
   }
-
-  // Cache this range's track span properties.
-  for (wtf_size_t i = 0; i < new_range.set_count; ++i) {
-    const auto& set_track_size =
-        sets_[new_range.begin_set_index + i].track_size;
-
-    // From https://drafts.csswg.org/css-grid-2/#algo-terms, a <flex> minimum
-    // sizing function shouldn't happen as it would be normalized to 'auto'.
-    DCHECK(!set_track_size.HasFlexMinTrackBreadth());
-
-    if (set_track_size.HasAutoMinTrackBreadth()) {
-      new_range.properties.SetProperty(
-          TrackSpanProperties::kHasAutoMinimumTrack);
-    }
-    if (set_track_size.HasFixedMinTrackBreadth()) {
-      new_range.properties.SetProperty(
-          TrackSpanProperties::kHasFixedMinimumTrack);
-    }
-    if (set_track_size.HasFixedMaxTrackBreadth()) {
-      new_range.properties.SetProperty(
-          TrackSpanProperties::kHasFixedMaximumTrack);
-    }
-    if (set_track_size.HasFlexMaxTrackBreadth()) {
-      new_range.properties.SetProperty(TrackSpanProperties::kHasFlexibleTrack);
-      new_range.properties.SetProperty(
-          TrackSpanProperties::kIsDependentOnAvailableSize);
-    }
-    if (set_track_size.HasIntrinsicMinTrackBreadth() ||
-        set_track_size.HasIntrinsicMaxTrackBreadth()) {
-      new_range.properties.SetProperty(TrackSpanProperties::kHasIntrinsicTrack);
-    }
-    if (!set_track_size.HasFixedMinTrackBreadth() ||
-        !set_track_size.HasFixedMaxTrackBreadth() ||
-        (set_track_size.MinTrackBreadth().length() !=
-         set_track_size.MaxTrackBreadth().length())) {
-      new_range.properties.SetProperty(
-          TrackSpanProperties::kHasNonDefiniteTrack);
-    }
-  }
-  ranges_.push_back(new_range);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
index 8070cf7..995209f 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
@@ -38,7 +38,7 @@
 
   GridTrackSizingDirection Direction() const { return track_direction_; }
 
- private:
+ protected:
   GridTrackSizingDirection track_direction_;
 };
 
@@ -58,30 +58,62 @@
   };
 
   inline bool HasProperty(PropertyId id) const { return bitmask_ & id; }
+  inline void Reset() { bitmask_ &= kIsCollapsed | kIsImplicit; }
   inline void SetProperty(PropertyId id) { bitmask_ |= id; }
-  inline void Reset() { bitmask_ = kNoPropertyId; }
+
+  inline TrackSpanProperties& operator|=(const TrackSpanProperties& other) {
+    bitmask_ |= other.bitmask_;
+    return *this;
+  }
 
  private:
   wtf_size_t bitmask_{kNoPropertyId};
 };
 
-class CORE_EXPORT NGGridBlockTrackCollection
-    : public NGGridTrackCollectionBase {
+struct CORE_EXPORT NGGridRange {
+  bool IsCollapsed() const;
+  bool IsImplicit() const;
+
+  void SetIsCollapsed();
+  void SetIsImplicit();
+
+  wtf_size_t begin_set_index;
+  wtf_size_t repeater_index;
+  wtf_size_t repeater_offset;
+  wtf_size_t set_count;
+  wtf_size_t start_line;
+  wtf_size_t track_count;
+
+  TrackSpanProperties properties;
+};
+
+using NGGridRangeVector = Vector<NGGridRange, 16>;
+
+class CORE_EXPORT NGGridRangeBuilder {
+  STACK_ALLOCATED();
+
  public:
-  struct CORE_EXPORT Range {
-    bool IsCollapsed() const;
-    bool IsImplicit() const;
+  NGGridRangeBuilder() = delete;
 
-    void SetIsCollapsed();
-    void SetIsImplicit();
+  NGGridRangeBuilder(const ComputedStyle& grid_style,
+                     const NGGridPlacementData& placement_data,
+                     GridTrackSizingDirection track_direction);
 
-    wtf_size_t start_line;
-    wtf_size_t track_count;
-    wtf_size_t repeater_index;
-    wtf_size_t repeater_offset;
+  // Ensures that after FinalizeRanges is called, a range will start at the
+  // |start_line|, a range will end at |start_line| + |span_length|.
+  // |grid_item_start_range_index| and |grid_item_end_range_index| will be
+  // written to during |FinalizeRanges|.
+  void EnsureTrackCoverage(wtf_size_t start_line,
+                           wtf_size_t span_length,
+                           wtf_size_t* grid_item_start_range_index,
+                           wtf_size_t* grid_item_end_range_index);
 
-    TrackSpanProperties properties;
-  };
+  // Build the collection of ranges based on information provided through the
+  // specified tracks and |EnsureTrackCoverage|.
+  NGGridRangeVector FinalizeRanges();
+
+ private:
+  friend class NGGridTrackCollectionTest;
 
   // This structure represents the grid line boundaries of a repeater or item
   // placed on the implicit grid. For the latter, a pointer to its respective
@@ -97,43 +129,15 @@
     wtf_size_t* grid_item_range_index_to_cache;
   };
 
-  NGGridBlockTrackCollection(const ComputedStyle& grid_style,
-                             const NGGridPlacementData& placement_data,
-                             GridTrackSizingDirection track_direction);
-
-  // NGGridTrackCollectionBase overrides.
-  wtf_size_t RangeCount() const override { return ranges_.size(); }
-  wtf_size_t RangeStartLine(wtf_size_t range_index) const override;
-  wtf_size_t RangeTrackCount(wtf_size_t range_index) const override;
-
-  // Ensures that after FinalizeRanges is called, a range will start at the
-  // |start_line|, a range will end at |start_line| + |span_length|.
-  // |grid_item_start_range_index| and |grid_item_end_range_index| will be
-  // written to during |FinalizeRanges|.
-  void EnsureTrackCoverage(wtf_size_t start_line,
-                           wtf_size_t span_length,
-                           wtf_size_t* grid_item_start_range_index,
-                           wtf_size_t* grid_item_end_range_index);
-  // Build the collection of ranges based on information provided through the
-  // specified tracks and |EnsureTrackCoverage|.
-  void FinalizeRanges();
-
-  const NGGridTrackList& ExplicitTracks() const { return explicit_tracks_; }
-  const NGGridTrackList& ImplicitTracks() const { return implicit_tracks_; }
-  const Vector<Range>& Ranges() const { return ranges_; }
-
- private:
-  friend class NGGridTrackCollectionTest;
-
   // This constructor is used exclusively in testing.
-  NGGridBlockTrackCollection(const NGGridTrackList& explicit_tracks,
-                             const NGGridTrackList& implicit_tracks,
-                             wtf_size_t auto_repetitions);
+  NGGridRangeBuilder(const NGGridTrackList& explicit_tracks,
+                     const NGGridTrackList& implicit_tracks,
+                     wtf_size_t auto_repetitions);
 
   wtf_size_t auto_repetitions_;
   wtf_size_t start_offset_;
 
-  bool track_indices_need_sort_ : 1;
+  bool must_sort_grid_lines : 1;
 
   // Stores the grid's explicit and implicit tracks.
   const NGGridTrackList& explicit_tracks_;
@@ -142,10 +146,8 @@
   // Starting and ending tracks mark where ranges will start and end.
   // The corresponding range_index will be written to during |FinalizeRanges|.
   // Once the ranges have been built in FinalizeRanges, these are cleared.
-  Vector<TrackBoundaryToRangePair> start_lines_;
-  Vector<TrackBoundaryToRangePair> end_lines_;
-
-  Vector<Range> ranges_;
+  Vector<TrackBoundaryToRangePair, 16> start_lines_;
+  Vector<TrackBoundaryToRangePair, 16> end_lines_;
 };
 
 class CORE_EXPORT NGGridLayoutTrackCollection
@@ -153,17 +155,6 @@
   USING_FAST_MALLOC(NGGridLayoutTrackCollection);
 
  public:
-  struct CORE_EXPORT Range {
-    bool IsCollapsed() const;
-
-    wtf_size_t set_count;
-    wtf_size_t start_line;
-    wtf_size_t track_count;
-    wtf_size_t begin_set_index;
-
-    TrackSpanProperties properties;
-  };
-
   struct SetGeometry {
     SetGeometry(LayoutUnit offset, wtf_size_t track_count)
         : offset(offset), track_count(track_count) {}
@@ -172,6 +163,8 @@
     wtf_size_t track_count;
   };
 
+  NGGridLayoutTrackCollection() = delete;
+
   explicit NGGridLayoutTrackCollection(GridTrackSizingDirection track_direction)
       : NGGridTrackCollectionBase(track_direction) {}
 
@@ -191,12 +184,8 @@
   wtf_size_t RangeSetCount(wtf_size_t range_index) const;
   // Return the index of the first set spanned by a given track range.
   wtf_size_t RangeBeginSetIndex(wtf_size_t range_index) const;
-
-  // Returns true if the specified property has been set in the track span
-  // properties bitmask of the range at position |range_index|.
-  bool RangeHasTrackSpanProperty(
-      wtf_size_t range_index,
-      TrackSpanProperties::PropertyId property_id) const;
+  // Returns the track span properties of the range at position |range_index|.
+  TrackSpanProperties RangeProperties(wtf_size_t range_index) const;
 
   wtf_size_t EndLineOfImplicitGrid() const;
   // Returns true if |grid_line| is contained within the implicit grid.
@@ -206,10 +195,7 @@
   LayoutUnit GetSetOffset(wtf_size_t set_index) const;
   wtf_size_t GetSetTrackCount(wtf_size_t set_index) const;
 
-  bool HasBaselines() const {
-    DCHECK_EQ(major_baselines_.empty(), minor_baselines_.empty());
-    return !major_baselines_.empty();
-  }
+  bool HasBaselines() const;
   LayoutUnit MajorBaseline(wtf_size_t set_index) const;
   LayoutUnit MinorBaseline(wtf_size_t set_index) const;
 
@@ -230,7 +216,7 @@
       GridTrackSizingDirection subgrid_track_direction) const;
 
   LayoutUnit GutterSize() const { return gutter_size_; }
-  const Vector<Range>& Ranges() const { return ranges_; }
+  const NGGridRangeVector& Ranges() const { return ranges_; }
 
   // Don't allow this class to be used for grid sizing.
   virtual bool IsForSizing() const { return false; }
@@ -249,16 +235,16 @@
   LayoutUnit start_extra_margin_;
   LayoutUnit end_extra_margin_;
 
-  Vector<Range> ranges_;
-  Vector<LayoutUnit> major_baselines_;
-  Vector<LayoutUnit> minor_baselines_;
-  Vector<SetGeometry> sets_geometry_;
+  NGGridRangeVector ranges_;
+  Vector<SetGeometry, 16> sets_geometry_;
+  Vector<LayoutUnit, 16> major_baselines_;
+  Vector<LayoutUnit, 16> minor_baselines_;
 };
 
-// |NGGridBlockTrackCollection::EnsureTrackCoverage| may introduce a range start
-// and/or end at the middle of any repeater from the block collection. This will
-// affect how some repeated tracks within the same repeater group resolve their
-// track sizes; e.g. consider the track list 'repeat(10, auto)' with a grid item
+// |NGGridRangeBuilder::EnsureTrackCoverage| may introduce a range start and/or
+// end at the middle of any repeater from the block collection. This will affect
+// how some repeated tracks within the same repeater group resolve their track
+// sizes; e.g. consider the track list 'repeat(10, auto)' with a grid item
 // spanning from the 3rd to the 7th track in the repeater, every track within
 // the item's range will grow to fit the content of that item first.
 //
@@ -288,8 +274,8 @@
 //   Range 4:  [6-8], Set 4: {  1fr (2) , 5px (1) }
 //   Range 5: [9-13], Set 5: { auto (5) }
 //
-// Note that, since |NGGridBlockTrackCollection|'s ranges are assured to span a
-// single repeater and to not cross any grid item's boundary in the respective
+// Note that, since |NGGridRangeBuilder|'s ranges are assured to span a single
+// repeater and to not cross any grid item's boundary in the respective
 // dimension, tracks within a set are "commutative" and can be sized evenly.
 struct CORE_EXPORT NGGridSet {
   explicit NGGridSet(wtf_size_t track_count);
@@ -376,16 +362,15 @@
   typedef SetIteratorBase<false> SetIterator;
   typedef SetIteratorBase<true> ConstSetIterator;
 
-  // |is_available_size_indefinite| is used to normalize percentage track
-  // sizing functions (see the constructor for |NGGridSet|).
-  NGGridSizingTrackCollection(
-      const NGGridBlockTrackCollection& block_track_collection,
-      bool is_available_size_indefinite);
-
+  NGGridSizingTrackCollection() = delete;
   NGGridSizingTrackCollection(const NGGridSizingTrackCollection&) = delete;
   NGGridSizingTrackCollection& operator=(const NGGridSizingTrackCollection&) =
       delete;
 
+  explicit NGGridSizingTrackCollection(
+      NGGridRangeVector&& ranges,
+      GridTrackSizingDirection track_direction = kForColumns);
+
   // Returns a reference to the set located at position |set_index|.
   NGGridSet& GetSetAt(wtf_size_t set_index);
   const NGGridSet& GetSetAt(wtf_size_t set_index) const;
@@ -407,12 +392,17 @@
   }
   LayoutUnit TotalTrackSize() const;
 
+  TrackSpanProperties InitializeSets(const ComputedStyle& grid_style,
+                                     LayoutUnit grid_available_size);
+  void SetIndefiniteGrowthLimitsToBaseSize();
+
   // Caches the initial geometry used to compute grid item contributions.
   void InitializeSetsGeometry(LayoutUnit first_set_offset,
                               LayoutUnit gutter_size);
   // Caches the final geometry required to place |NGGridSet| in the grid.
   void CacheSetsGeometry(LayoutUnit first_set_offset, LayoutUnit gutter_size);
-  void SetIndefiniteGrowthLimitsToBaseSize();
+  // Returns true if any of the two methods above already cached sets' geometry.
+  bool HasCachedSetsGeometry() const { return !sets_geometry_.empty(); }
 
   void ResetBaselines();
 
@@ -422,10 +412,17 @@
   void SetGutterSize(LayoutUnit gutter_size) { gutter_size_ = gutter_size; }
 
  private:
-  void AppendTrackRange(
-      const NGGridBlockTrackCollection::Range& block_track_range,
-      const NGGridTrackList& specified_track_list,
-      bool is_available_size_indefinite);
+  friend class NGGridTrackCollectionTest;
+
+  // This private version of |InitializeSets| is directly used in testing.
+  TrackSpanProperties InitializeSets(
+      const NGGridTrackList& explicit_track_list,
+      const NGGridTrackList& implicit_track_list,
+      LayoutUnit grid_available_size = kIndefiniteSize);
+
+  void AppendSetsForRange(const NGGridTrackList& specified_track_list,
+                          bool is_available_size_indefinite,
+                          NGGridRange* range);
 
   wtf_size_t non_collapsed_track_count_;
 
@@ -452,12 +449,12 @@
   //  start: 5, end: 6 -> 100px
   //  start: 3, end: 5 -> indefinite as:
   //    "start <= sets[end].last_indefinite_index"
-  Vector<wtf_size_t> last_indefinite_indices_;
+  Vector<wtf_size_t, 16> last_indefinite_indices_;
 
   // A vector of every set element that compose the entire collection's ranges;
   // track definitions from the same set are stored in consecutive positions,
   // preserving the order in which the definitions appear in their range.
-  Vector<NGGridSet> sets_;
+  Vector<NGGridSet, 16> sets_;
 };
 
 template <>
@@ -469,8 +466,7 @@
 
 }  // namespace blink
 
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
-    blink::NGGridLayoutTrackCollection::Range)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGGridRange)
 WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
     blink::NGGridLayoutTrackCollection::SetGeometry)
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
index c38f861..2f0b5d2 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
@@ -62,26 +62,33 @@
 
 class NGGridTrackCollectionTest : public NGLayoutTest {
  protected:
-  NGGridBlockTrackCollection CreateBlockTrackCollection(
-      const NGGridTrackList& explicit_tracks,
-      const NGGridTrackList& implicit_tracks,
-      wtf_size_t auto_repetitions) {
-    return NGGridBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                      auto_repetitions);
+  NGGridRangeBuilder CreateRangeBuilder(const NGGridTrackList& explicit_tracks,
+                                        const NGGridTrackList& implicit_tracks,
+                                        wtf_size_t auto_repetitions) {
+    return NGGridRangeBuilder(explicit_tracks, implicit_tracks,
+                              auto_repetitions);
   }
 
   Vector<GridTrackSize, 1> CreateTrackSizes(wtf_size_t track_count) {
     return {track_count, GridTrackSize(Length::Auto())};
   }
 
+  void InitializeSetsForSizingCollection(
+      const NGGridTrackList& explicit_tracks,
+      const NGGridTrackList& implicit_tracks,
+      NGGridSizingTrackCollection* sizing_collection) {
+    sizing_collection->InitializeSets(explicit_tracks, implicit_tracks);
+  }
+
   NGGridSizingTrackCollection::SetIterator IteratorForRange(
       NGGridSizingTrackCollection& track_collection,
       wtf_size_t range_index) {
-    wtf_size_t starting_set_index =
+    const wtf_size_t begin_set_index =
         track_collection.RangeBeginSetIndex(range_index);
+
     return track_collection.GetSetIterator(
-        starting_set_index,
-        starting_set_index + track_collection.RangeSetCount(range_index));
+        begin_set_index,
+        begin_set_index + track_collection.RangeSetCount(range_index));
   }
 };
 
@@ -168,7 +175,7 @@
   ASSERT_EQ(3u, track_list.RepeaterCount());
 }
 
-TEST_F(NGGridTrackCollectionTest, TestNGGridBlockTrackCollection) {
+TEST_F(NGGridTrackCollectionTest, TestNGGridRangeBuilder) {
   NGGridTrackList explicit_tracks, implicit_tracks;
   ASSERT_TRUE(explicit_tracks.AddRepeater(
       CreateTrackSizes(2), NGGridTrackRepeater::RepeatType::kInteger, 4));
@@ -176,18 +183,16 @@
       CreateTrackSizes(3), NGGridTrackRepeater::RepeatType::kAutoFill));
   ASSERT_EQ(2u, explicit_tracks.RepeaterCount());
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 3);
-  block_collection.FinalizeRanges();
-  const auto& ranges = block_collection.Ranges();
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 3);
+  const auto& ranges = range_builder.FinalizeRanges();
 
   EXPECT_EQ(2u, ranges.size());
   EXPECT_RANGE(0u, 8u, ranges[0]);
   EXPECT_RANGE(8u, 9u, ranges[1]);
 }
 
-TEST_F(NGGridTrackCollectionTest, TestNGGridBlockTrackCollectionCollapsed) {
+TEST_F(NGGridTrackCollectionTest, TestNGGridRangeBuilderCollapsed) {
   NGGridTrackList explicit_tracks, implicit_tracks;
   ASSERT_TRUE(explicit_tracks.AddRepeater(
       CreateTrackSizes(2), NGGridTrackRepeater::RepeatType::kInteger, 4));
@@ -197,11 +202,9 @@
       CreateTrackSizes(3), NGGridTrackRepeater::RepeatType::kInteger, 7));
   ASSERT_EQ(3u, explicit_tracks.RepeaterCount());
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 3);
-  block_collection.FinalizeRanges();
-  const auto& ranges = block_collection.Ranges();
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 3);
+  const auto& ranges = range_builder.FinalizeRanges();
 
   EXPECT_EQ(3u, ranges.size());
   EXPECT_RANGE(0u, 8u, ranges[0]);
@@ -209,7 +212,7 @@
   EXPECT_RANGE(17u, 21u, ranges[2]);
 }
 
-TEST_F(NGGridTrackCollectionTest, TestNGGridBlockTrackCollectionImplicit) {
+TEST_F(NGGridTrackCollectionTest, TestNGGridRangeBuilderImplicit) {
   NGGridTrackList explicit_tracks;
   ASSERT_TRUE(explicit_tracks.AddRepeater(
       CreateTrackSizes(2), NGGridTrackRepeater::RepeatType::kInteger, 4));
@@ -223,22 +226,19 @@
   ASSERT_TRUE(implicit_tracks.AddRepeater(
       CreateTrackSizes(8), NGGridTrackRepeater::RepeatType::kInteger, 2));
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 3);
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 3);
 
   wtf_size_t range1_start, range1_end, range2_start, range2_end;
-  block_collection.EnsureTrackCoverage(3, 40, &range1_start, &range1_end);
-  block_collection.EnsureTrackCoverage(3, 40, &range2_start, &range2_end);
-  block_collection.FinalizeRanges();
+  range_builder.EnsureTrackCoverage(3, 40, &range1_start, &range1_end);
+  range_builder.EnsureTrackCoverage(3, 40, &range2_start, &range2_end);
+  const auto& ranges = range_builder.FinalizeRanges();
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(4u, range1_end);
   EXPECT_EQ(1u, range2_start);
   EXPECT_EQ(4u, range2_end);
 
-  const auto& ranges = block_collection.Ranges();
-
   EXPECT_EQ(5u, ranges.size());
   EXPECT_RANGE(0u, 3u, ranges[0]);
   EXPECT_FALSE(ranges[0].IsImplicit());
@@ -317,13 +317,12 @@
   }
   ASSERT_EQ(set_counts.size(), explicit_tracks.RepeaterCount());
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 0);
-  block_collection.FinalizeRanges();
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 0);
 
-  NGGridSizingTrackCollection track_collection(
-      block_collection, /* is_content_box_size_defined */ false);
+  NGGridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
+                                    &track_collection);
   const auto& ranges = track_collection.Ranges();
 
   // Test the set iterator for the entire collection.
@@ -372,24 +371,20 @@
       track_sizes, NGGridTrackRepeater::RepeatType::kAutoFit));
   ASSERT_EQ(2u, explicit_tracks.RepeaterCount());
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 5);
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 5);
 
-  wtf_size_t range1_start;
-  wtf_size_t range1_end;
-  wtf_size_t range2_start;
-  wtf_size_t range2_end;
-  wtf_size_t range3_start;
-  wtf_size_t range3_end;
-  wtf_size_t range4_start;
-  wtf_size_t range4_end;
+  wtf_size_t range1_start, range1_end, range2_start, range2_end, range3_start,
+      range3_end, range4_start, range4_end;
+  range_builder.EnsureTrackCoverage(2, 4, &range1_start, &range1_end);
+  range_builder.EnsureTrackCoverage(12, 4, &range2_start, &range2_end);
+  range_builder.EnsureTrackCoverage(17, 3, &range3_start, &range3_end);
+  range_builder.EnsureTrackCoverage(22, 5, &range4_start, &range4_end);
 
-  block_collection.EnsureTrackCoverage(2, 4, &range1_start, &range1_end);
-  block_collection.EnsureTrackCoverage(12, 4, &range2_start, &range2_end);
-  block_collection.EnsureTrackCoverage(17, 3, &range3_start, &range3_end);
-  block_collection.EnsureTrackCoverage(22, 5, &range4_start, &range4_end);
-  block_collection.FinalizeRanges();
+  NGGridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
+                                    &track_collection);
+  const auto& ranges = track_collection.Ranges();
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(1u, range1_end);
@@ -400,10 +395,6 @@
   EXPECT_EQ(9u, range4_start);
   EXPECT_EQ(9u, range4_end);
 
-  NGGridSizingTrackCollection track_collection(
-      block_collection, /* is_content_box_size_defined */ false);
-  const auto& ranges = track_collection.Ranges();
-
   EXPECT_EQ(10u, ranges.size());
   EXPECT_RANGE(0u, 2u, ranges[0]);
   auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0);
@@ -489,28 +480,23 @@
       track_sizes, NGGridTrackRepeater::RepeatType::kNoRepeat, 1));
   ASSERT_EQ(1u, implicit_tracks.RepeaterCount());
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 0);
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 0);
 
-  wtf_size_t range1_start;
-  wtf_size_t range1_end;
-  wtf_size_t range2_start;
-  wtf_size_t range2_end;
+  wtf_size_t range1_start, range1_end, range2_start, range2_end;
+  range_builder.EnsureTrackCoverage(2, 13, &range1_start, &range1_end);
+  range_builder.EnsureTrackCoverage(23, 2, &range2_start, &range2_end);
 
-  block_collection.EnsureTrackCoverage(2, 13, &range1_start, &range1_end);
-  block_collection.EnsureTrackCoverage(23, 2, &range2_start, &range2_end);
-  block_collection.FinalizeRanges();
+  NGGridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
+                                    &track_collection);
+  const auto& ranges = track_collection.Ranges();
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(2u, range1_end);
   EXPECT_EQ(4u, range2_start);
   EXPECT_EQ(4u, range2_end);
 
-  NGGridSizingTrackCollection track_collection(
-      block_collection, /* is_content_box_size_defined */ false);
-  const auto& ranges = track_collection.Ranges();
-
   EXPECT_EQ(5u, ranges.size());
   EXPECT_RANGE(0u, 2u, ranges[0]);
   auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0);
@@ -566,28 +552,23 @@
       track_sizes, NGGridTrackRepeater::RepeatType::kInteger, 2));
   ASSERT_EQ(1u, explicit_tracks.RepeaterCount());
 
-  NGGridBlockTrackCollection block_collection =
-      CreateBlockTrackCollection(explicit_tracks, implicit_tracks,
-                                 /* auto_repetitions */ 0);
+  auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
+                                          /* auto_repetitions */ 0);
 
-  wtf_size_t range1_start;
-  wtf_size_t range1_end;
-  wtf_size_t range2_start;
-  wtf_size_t range2_end;
+  wtf_size_t range1_start, range1_end, range2_start, range2_end;
+  range_builder.EnsureTrackCoverage(1, 2, &range1_start, &range1_end);
+  range_builder.EnsureTrackCoverage(7, 4, &range2_start, &range2_end);
 
-  block_collection.EnsureTrackCoverage(1, 2, &range1_start, &range1_end);
-  block_collection.EnsureTrackCoverage(7, 4, &range2_start, &range2_end);
-  block_collection.FinalizeRanges();
+  NGGridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
+                                    &track_collection);
+  const auto& ranges = track_collection.Ranges();
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(1u, range1_end);
   EXPECT_EQ(3u, range2_start);
   EXPECT_EQ(4u, range2_end);
 
-  NGGridSizingTrackCollection track_collection(
-      block_collection, /* is_content_box_size_defined */ false);
-  const auto& ranges = track_collection.Ranges();
-
   EXPECT_EQ(5u, ranges.size());
   EXPECT_RANGE(0u, 1u, ranges[0]);
   auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0);
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index e91a5bf..3f3701c 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -58,7 +58,7 @@
   static_assert(
       std::is_base_of<LayoutBlock, Base>::value,
       "Base class of LayoutNGMixin must be LayoutBlock or derived class.");
-  if (node && node->IsElementNode())
+  if (node)
     Base::GetDocument().IncLayoutBlockCounterNG();
 }
 
diff --git a/third_party/blink/renderer/core/messaging/message_port.cc b/third_party/blink/renderer/core/messaging/message_port.cc
index 6da4919..b38bbd1d 100644
--- a/third_party/blink/renderer/core/messaging/message_port.cc
+++ b/third_party/blink/renderer/core/messaging/message_port.cc
@@ -384,6 +384,9 @@
     }
   }
 
+  if (!message.message->CanDeserializeIn(context))
+    return MessageEvent::CreateError();
+
   MessagePortArray* ports = MessagePort::EntanglePorts(
       *GetExecutionContext(), std::move(message.ports));
   UserActivation* user_activation = nullptr;
diff --git a/third_party/blink/renderer/core/messaging/message_port_test.cc b/third_party/blink/renderer/core/messaging/message_port_test.cc
index f045c5f3..9d7eaa50 100644
--- a/third_party/blink/renderer/core/messaging/message_port_test.cc
+++ b/third_party/blink/renderer/core/messaging/message_port_test.cc
@@ -5,8 +5,10 @@
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/messaging/transferable_message.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -67,5 +69,33 @@
   EXPECT_EQ(wait->GetLastEvent()->type(), event_type_names::kMessageerror);
 }
 
+TEST(MessagePortTest, DispatchMessageErrorEvent_CannotDeserialize) {
+  DummyPageHolder holder;
+  LocalDOMWindow* window = holder.GetFrame().DomWindow();
+  MessagePort* port = MakeGarbageCollected<MessagePort>(*window);
+
+  SerializedScriptValue::ScopedOverrideCanDeserializeInForTesting
+      override_can_deserialize_in(base::BindLambdaForTesting(
+          [&](const SerializedScriptValue& value,
+              ExecutionContext* execution_context, bool can_deserialize) {
+            EXPECT_EQ(execution_context, window);
+            EXPECT_TRUE(can_deserialize);
+            return false;
+          }));
+
+  base::RunLoop run_loop;
+  auto* wait = MakeGarbageCollected<WaitForEvent>();
+  wait->AddEventListener(port, event_type_names::kMessage);
+  wait->AddEventListener(port, event_type_names::kMessageerror);
+  wait->AddCompletionClosure(run_loop.QuitClosure());
+
+  mojo::Message mojo_message =
+      mojom::blink::TransferableMessage::WrapAsMessage(MakeNullMessage());
+  ASSERT_TRUE(static_cast<mojo::MessageReceiver*>(port)->Accept(&mojo_message));
+  run_loop.Run();
+
+  EXPECT_EQ(wait->GetLastEvent()->type(), event_type_names::kMessageerror);
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 8f62d7a..b774f10a15c 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -1916,7 +1916,7 @@
 
   if (scroll_corner_) {
     LayoutRect rect(ScrollCornerRect());
-    scroll_corner_->SetFrameRect(rect);
+    scroll_corner_->SetOverriddenFrameRect(rect);
     // TODO(crbug.com/1020913): This should be part of PaintPropertyTreeBuilder
     // when we support subpixel layout of overflow controls.
     scroll_corner_->GetMutableForPainting().FirstFragment().SetPaintOffset(
@@ -1925,7 +1925,7 @@
 
   if (resizer_) {
     LayoutRect rect(ResizerCornerRect(kResizerForPointer));
-    resizer_->SetFrameRect(rect);
+    resizer_->SetOverriddenFrameRect(rect);
     // TODO(crbug.com/1020913): This should be part of PaintPropertyTreeBuilder
     // when we support subpixel layout of overflow controls.
     resizer_->GetMutableForPainting().FirstFragment().SetPaintOffset(
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
index 8d836f3..f97bad0d 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
@@ -221,8 +221,12 @@
   MessagePortArray* ports = MessagePort::EntanglePorts(
       *GetExecutionContext(), std::move(message.ports));
   debugger->ExternalAsyncTaskStarted(message.sender_stack_trace_id);
-  worker_object_->DispatchEvent(
-      *MessageEvent::Create(ports, std::move(message.message)));
+  if (message.message->CanDeserializeIn(GetExecutionContext())) {
+    worker_object_->DispatchEvent(
+        *MessageEvent::Create(ports, std::move(message.message)));
+  } else {
+    worker_object_->DispatchEvent(*MessageEvent::CreateError());
+  }
   debugger->ExternalAsyncTaskFinished(message.sender_stack_trace_id);
 }
 
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
index 137243e..8d997be 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/bind.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
@@ -362,6 +363,32 @@
   EXPECT_EQ(wait->GetLastEvent()->type(), event_type_names::kMessage);
 }
 
+TEST_F(DedicatedWorkerTest,
+       DispatchMessageEventOnWorkerObject_CannotDeserialize) {
+  StartWorker();
+
+  base::RunLoop run_loop;
+  auto* wait = MakeGarbageCollected<WaitForEvent>();
+  wait->AddEventListener(WorkerObject(), event_type_names::kMessage);
+  wait->AddEventListener(WorkerObject(), event_type_names::kMessageerror);
+  wait->AddCompletionClosure(run_loop.QuitClosure());
+
+  SerializedScriptValue::ScopedOverrideCanDeserializeInForTesting
+      override_can_deserialize_in(base::BindLambdaForTesting(
+          [&](const SerializedScriptValue&, ExecutionContext* execution_context,
+              bool can_deserialize) {
+            EXPECT_EQ(execution_context, GetFrame().DomWindow());
+            EXPECT_TRUE(can_deserialize);
+            return false;
+          }));
+  auto message = MakeTransferableMessage(
+      GetDocument().GetExecutionContext()->GetAgentClusterID());
+  WorkerMessagingProxy()->PostMessageToWorkerObject(std::move(message));
+  run_loop.Run();
+
+  EXPECT_EQ(wait->GetLastEvent()->type(), event_type_names::kMessageerror);
+}
+
 TEST_F(DedicatedWorkerTest, DispatchMessageEventOnWorkerGlobalScope) {
   // Script must run for the worker global scope to dispatch messages.
   const String source_code = "// Do nothing";
@@ -410,4 +437,63 @@
   EXPECT_EQ(event_type, event_type_names::kMessage);
 }
 
+TEST_F(DedicatedWorkerTest,
+       DispatchMessageEventOnWorkerGlobalScope_CannotDeserialize) {
+  // Script must run for the worker global scope to dispatch messages.
+  const String source_code = "// Do nothing";
+  StartWorker();
+  EvaluateClassicScript(source_code);
+
+  AtomicString event_type;
+  base::RunLoop run_loop_1;
+  base::RunLoop run_loop_2;
+
+  auto* worker_thread = GetWorkerThread();
+  SerializedScriptValue::ScopedOverrideCanDeserializeInForTesting
+      override_can_deserialize_in(base::BindLambdaForTesting(
+          [&](const SerializedScriptValue&, ExecutionContext* execution_context,
+              bool can_deserialize) {
+            EXPECT_EQ(execution_context, worker_thread->GlobalScope());
+            EXPECT_TRUE(can_deserialize);
+            return false;
+          }));
+
+  PostCrossThreadTask(
+      *GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
+      CrossThreadBindOnce(
+          [](DedicatedWorkerThreadForTest* worker_thread,
+             AtomicString* event_type, WTF::CrossThreadOnceClosure quit_1,
+             WTF::CrossThreadOnceClosure quit_2) {
+            auto* global_scope = worker_thread->GlobalScope();
+            auto* wait = MakeGarbageCollected<WaitForEvent>();
+            wait->AddEventListener(global_scope, event_type_names::kMessage);
+            wait->AddEventListener(global_scope,
+                                   event_type_names::kMessageerror);
+            wait->AddCompletionClosure(WTF::BindOnce(
+                [](WaitForEvent* wait, AtomicString* event_type,
+                   WTF::CrossThreadOnceClosure quit_closure) {
+                  *event_type = wait->GetLastEvent()->type();
+                  std::move(quit_closure).Run();
+                },
+                WrapPersistent(wait), WTF::Unretained(event_type),
+                std::move(quit_2)));
+            std::move(quit_1).Run();
+          },
+          CrossThreadUnretained(worker_thread),
+          CrossThreadUnretained(&event_type),
+          WTF::CrossThreadOnceClosure(run_loop_1.QuitClosure()),
+          WTF::CrossThreadOnceClosure(run_loop_2.QuitClosure())));
+
+  // Wait for the first run loop to quit, which signals that the event listeners
+  // are registered. Then post the message and wait to be notified of the
+  // result. Each run loop can only be used once.
+  run_loop_1.Run();
+  auto message = MakeTransferableMessage(
+      GetDocument().GetExecutionContext()->GetAgentClusterID());
+  WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
+  run_loop_2.Run();
+
+  EXPECT_EQ(event_type, event_type_names::kMessageerror);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 7a30ea77..a3d8f2a 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -552,14 +552,20 @@
       WorkerThreadDebugger::From(GetThread()->GetIsolate());
   if (debugger)
     debugger->ExternalAsyncTaskStarted(message.sender_stack_trace_id);
-  UserActivation* user_activation = nullptr;
-  if (message.user_activation) {
-    user_activation = MakeGarbageCollected<UserActivation>(
-        message.user_activation->has_been_active,
-        message.user_activation->was_active);
+
+  if (message.message->CanDeserializeIn(this)) {
+    UserActivation* user_activation = nullptr;
+    if (message.user_activation) {
+      user_activation = MakeGarbageCollected<UserActivation>(
+          message.user_activation->has_been_active,
+          message.user_activation->was_active);
+    }
+    DispatchEvent(*MessageEvent::Create(ports, std::move(message.message),
+                                        user_activation));
+  } else {
+    DispatchEvent(*MessageEvent::CreateError());
   }
-  DispatchEvent(*MessageEvent::Create(ports, std::move(message.message),
-                                      user_activation));
+
   if (debugger)
     debugger->ExternalAsyncTaskFinished(message.sender_stack_trace_id);
 }
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
index d2be850..1b56281 100644
--- a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
+++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
@@ -160,17 +160,17 @@
 }
 
 void BroadcastChannel::OnMessage(BlinkCloneableMessage message) {
+  auto* context = GetExecutionContext();
+
   // Queue a task to dispatch the event.
   MessageEvent* event;
-  if (!message.locked_to_sender_agent_cluster ||
-      GetExecutionContext()->IsSameAgentCluster(
-          message.sender_agent_cluster_id)) {
-    event = MessageEvent::Create(
-        nullptr, std::move(message.message),
-        GetExecutionContext()->GetSecurityOrigin()->ToString());
+  if ((!message.locked_to_sender_agent_cluster ||
+       context->IsSameAgentCluster(message.sender_agent_cluster_id)) &&
+      message.message->CanDeserializeIn(context)) {
+    event = MessageEvent::Create(nullptr, std::move(message.message),
+                                 context->GetSecurityOrigin()->ToString());
   } else {
-    event = MessageEvent::CreateError(
-        GetExecutionContext()->GetSecurityOrigin()->ToString());
+    event = MessageEvent::CreateError(context->GetSecurityOrigin()->ToString());
   }
   // <specdef
   // href="https://html.spec.whatwg.org/multipage/web-messaging.html#dom-broadcastchannel-postmessage">
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc
index 8077730..0e653599 100644
--- a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc
+++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc
@@ -7,6 +7,7 @@
 #include <iterator>
 
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -178,6 +179,30 @@
             event_type_names::kMessageerror);
 }
 
+TEST(BroadcastChannelTest, MessageCannotDeserialize) {
+  DummyPageHolder holder;
+  LocalDOMWindow* window = holder.GetFrame().DomWindow();
+  auto* tester = MakeGarbageCollected<BroadcastChannelTester>(window);
+
+  SerializedScriptValue::ScopedOverrideCanDeserializeInForTesting
+      override_can_deserialize_in(base::BindLambdaForTesting(
+          [&](const SerializedScriptValue& value,
+              ExecutionContext* execution_context, bool can_deserialize) {
+            EXPECT_EQ(execution_context, window);
+            EXPECT_TRUE(can_deserialize);
+            return false;
+          }));
+
+  base::RunLoop run_loop;
+  tester->AwaitNextUpdate(run_loop.QuitClosure());
+  tester->PostMessage(MakeNullMessage());
+  run_loop.Run();
+
+  ASSERT_EQ(tester->received_events().size(), 1u);
+  EXPECT_EQ(tester->received_events()[0]->type(),
+            event_type_names::kMessageerror);
+}
+
 TEST(BroadcastChannelTest, OutgoingMessagesMarkedWithAgentClusterId) {
   DummyPageHolder holder;
   ExecutionContext* execution_context = holder.GetFrame().DomWindow();
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_container.cc b/third_party/blink/renderer/modules/service_worker/service_worker_container.cc
index d0aa0c5..5724250 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_container.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_container.cc
@@ -673,17 +673,16 @@
     }
   }
   if (!event) {
-    if (!msg.locked_to_sender_agent_cluster ||
-        GetExecutionContext()->IsSameAgentCluster(
-            msg.sender_agent_cluster_id)) {
-      event = MessageEvent::Create(
-          ports, std::move(msg.message),
-          GetExecutionContext()->GetSecurityOrigin()->ToString(),
-          String() /* lastEventId */, service_worker);
+    auto* context = GetExecutionContext();
+    if ((!msg.locked_to_sender_agent_cluster ||
+         context->IsSameAgentCluster(msg.sender_agent_cluster_id)) &&
+        msg.message->CanDeserializeIn(context)) {
+      event = MessageEvent::Create(ports, std::move(msg.message),
+                                   context->GetSecurityOrigin()->ToString(),
+                                   String() /* lastEventId */, service_worker);
     } else {
       event = MessageEvent::CreateError(
-          GetExecutionContext()->GetSecurityOrigin()->ToString(),
-          service_worker);
+          context->GetSecurityOrigin()->ToString(), service_worker);
     }
   }
   // Schedule the event to be dispatched on the correct task source:
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc b/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc
index 679fcfc..8e1a9f58 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_container_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/test/bind.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider.h"
@@ -510,5 +511,35 @@
   EXPECT_EQ(event->type(), event_type_names::kMessageerror);
 }
 
+TEST_F(ServiceWorkerContainerTest, ReceiveMessageWhichCannotDeserialize) {
+  SetPageURL("http://localhost/x/index.html");
+
+  StubWebServiceWorkerProvider stub_provider;
+  LocalDOMWindow* window = GetFrame().DomWindow();
+  ServiceWorkerContainer* container = ServiceWorkerContainer::CreateForTesting(
+      *window, stub_provider.Provider());
+
+  SerializedScriptValue::ScopedOverrideCanDeserializeInForTesting
+      override_can_deserialize_in(base::BindLambdaForTesting(
+          [&](const SerializedScriptValue& value,
+              ExecutionContext* execution_context, bool can_deserialize) {
+            EXPECT_EQ(execution_context, window);
+            EXPECT_TRUE(can_deserialize);
+            return false;
+          }));
+
+  base::RunLoop run_loop;
+  auto* wait = MakeGarbageCollected<WaitForEvent>();
+  wait->AddEventListener(container, event_type_names::kMessage);
+  wait->AddEventListener(container, event_type_names::kMessageerror);
+  wait->AddCompletionClosure(run_loop.QuitClosure());
+  container->ReceiveMessage(MakeServiceWorkerObjectInfo(),
+                            MakeTransferableMessage());
+  run_loop.Run();
+
+  auto* event = wait->GetLastEvent();
+  EXPECT_EQ(event->type(), event_type_names::kMessageerror);
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index ecf1b5a..672a82f 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -41,52 +41,72 @@
 #include "third_party/blink/renderer/platform/scheduler/public/main_thread.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
 
 namespace {
 
-void CreateContextProviderOnMainThread(
-    ExecutionContext* execution_context,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    CrossThreadOnceFunction<void(std::unique_ptr<WebGraphicsContext3DProvider>)>
-        callback) {
+void CreateContextProvider(
+    const KURL& url,
+    base::WaitableEvent* waitable_event,
+    std::unique_ptr<WebGraphicsContext3DProvider>* created_context_provider) {
   DCHECK(IsMainThread());
-  const KURL& url = execution_context->Url();
-  PostCrossThreadTask(
-      *task_runner, FROM_HERE,
-      CrossThreadBindOnce(
-          std::move(callback),
-          Platform::Current()->CreateWebGPUGraphicsContext3DProvider(url)));
+  *created_context_provider =
+      Platform::Current()->CreateWebGPUGraphicsContext3DProvider(url);
+  waitable_event->Signal();
 }
 
-void EnsureDawnControlClientInitialized(
-    ExecutionContext* execution_context,
-    base::OnceCallback<void(std::unique_ptr<WebGraphicsContext3DProvider>)>
-        callback) {
+std::unique_ptr<WebGraphicsContext3DProvider> CreateContextProviderOnMainThread(
+    const KURL& url) {
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      Thread::MainThread()->GetDeprecatedTaskRunner();
+
+  base::WaitableEvent waitable_event;
+  std::unique_ptr<WebGraphicsContext3DProvider> created_context_provider;
+  PostCrossThreadTask(
+      *task_runner, FROM_HERE,
+      CrossThreadBindOnce(&CreateContextProvider, url,
+                          CrossThreadUnretained(&waitable_event),
+                          CrossThreadUnretained(&created_context_provider)));
+
+  waitable_event.Wait();
+  return created_context_provider;
+}
+
+std::unique_ptr<WebGraphicsContext3DProvider> CreateContextProvider(
+    ExecutionContext& execution_context) {
+  const KURL& url = execution_context.Url();
+  std::unique_ptr<WebGraphicsContext3DProvider> context_provider;
   if (IsMainThread()) {
-    const KURL& url = execution_context->Url();
-    std::move(callback).Run(
-        Platform::Current()->CreateWebGPUGraphicsContext3DProvider(url));
+    context_provider =
+        Platform::Current()->CreateWebGPUGraphicsContext3DProvider(url);
   } else {
-    // Posts a task to the main thread to create context provider
-    // because the current RendererBlinkPlatformImpl and viz::Gpu
-    // APIs allow to create it only on the main thread.
-    // When it is created, posts it back to the current thread
-    // and call the callback with it.
-    // TODO(takahiro): Directly create context provider on Workers threads
-    //                 if RendererBlinkPlatformImpl and viz::Gpu will start to
-    //                 allow the context provider creation on Workers.
-    PostCrossThreadTask(
-        *Thread::MainThread()->GetDeprecatedTaskRunner(), FROM_HERE,
-        CrossThreadBindOnce(&CreateContextProviderOnMainThread,
-                            WrapCrossThreadPersistent(execution_context),
-                            execution_context->GetTaskRunner(TaskType::kWebGPU),
-                            CrossThreadBindOnce(std::move(callback))));
+    context_provider = CreateContextProviderOnMainThread(url);
   }
+
+  // Note that we check for API blocking *after* creating the context. This is
+  // because context creation synchronizes against GpuProcessHost lifetime in
+  // the browser process, and GpuProcessHost destruction is what updates API
+  // blocking state on a GPU process crash. See https://crbug.com/1215907#c10
+  // for more details.
+  bool blocked = true;
+  mojo::Remote<mojom::blink::GpuDataManager> gpu_data_manager;
+  Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
+      gpu_data_manager.BindNewPipeAndPassReceiver());
+  gpu_data_manager->Are3DAPIsBlockedForUrl(url, &blocked);
+  if (blocked) {
+    return nullptr;
+  }
+
+  // TODO(kainino): we will need a better way of accessing the GPU interface
+  // from multiple threads than BindToCurrentSequence et al.
+  if (context_provider && !context_provider->BindToCurrentSequence()) {
+    // TODO(crbug.com/973017): Collect GPU info and surface context creation
+    // error.
+    return nullptr;
+  }
+  return context_provider;
 }
 
 [[maybe_unused]] void AddConsoleWarning(ExecutionContext* execution_context,
@@ -267,95 +287,34 @@
       .Record(context->UkmRecorder());
 }
 
-std::unique_ptr<WebGraphicsContext3DProvider> CheckContextProvider(
-    const KURL& url,
-    std::unique_ptr<WebGraphicsContext3DProvider> context_provider) {
-  // Note that we check for API blocking *after* creating the context. This is
-  // because context creation synchronizes against GpuProcessHost lifetime in
-  // the browser process, and GpuProcessHost destruction is what updates API
-  // blocking state on a GPU process crash. See https://crbug.com/1215907#c10
-  // for more details.
-  bool blocked = true;
-  mojo::Remote<mojom::blink::GpuDataManager> gpu_data_manager;
-  Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
-      gpu_data_manager.BindNewPipeAndPassReceiver());
-  gpu_data_manager->Are3DAPIsBlockedForUrl(url, &blocked);
-  if (blocked) {
-    return nullptr;
-  }
-
-  // TODO(kainino): we will need a better way of accessing the GPU interface
-  // from multiple threads than BindToCurrentSequence et al.
-  if (context_provider && !context_provider->BindToCurrentSequence()) {
-    // TODO(crbug.com/973017): Collect GPU info and surface context creation
-    // error.
-    return nullptr;
-  }
-
-  return context_provider;
-}
-
-void GPU::RequestAdapterImpl(ScriptState* script_state,
-                             const GPURequestAdapterOptions* options,
-                             ScriptPromiseResolver* resolver) {
+ScriptPromise GPU::requestAdapter(ScriptState* script_state,
+                                  const GPURequestAdapterOptions* options) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
+
   if (!dawn_control_client_ || dawn_control_client_->IsContextLost()) {
-    dawn_control_client_initialized_callbacks_.push_back(WTF::BindOnce(
-        [](GPU* gpu, ScriptState* script_state,
-           const GPURequestAdapterOptions* options,
-           ScriptPromiseResolver* resolver) {
-          if (gpu->dawn_control_client_ &&
-              !gpu->dawn_control_client_->IsContextLost()) {
-            gpu->RequestAdapterImpl(script_state, options, resolver);
-          } else {
-            // Failed to create context provider, won't be able to request
-            // adapter
-            // TODO(crbug.com/973017): Collect GPU info and surface context
-            // creation error.
-            resolver->Resolve(v8::Null(script_state->GetIsolate()));
-          }
-        },
-        WrapPersistent(this), WrapPersistent(script_state),
-        WrapPersistent(options), WrapPersistent(resolver)));
-
-    // Returning since the task to create the control client from a previous
-    // call to EnsureDawnControlClientInitialized should be already running
-    if (dawn_control_client_initialized_callbacks_.size() > 1) {
-      return;
-    }
-
     // TODO(natlee@microsoft.com): if GPU process is lost, wait for the GPU
     // process to come back instead of rejecting right away
+    std::unique_ptr<WebGraphicsContext3DProvider> context_provider =
+        CreateContextProvider(*execution_context);
 
-    EnsureDawnControlClientInitialized(
-        execution_context,
-        WTF::BindOnce(
-            [](GPU* gpu, ExecutionContext* execution_context,
-               std::unique_ptr<WebGraphicsContext3DProvider> context_provider) {
-              const KURL& url = execution_context->Url();
-              context_provider =
-                  CheckContextProvider(url, std::move(context_provider));
-              if (context_provider) {
-                context_provider->WebGPUInterface()
-                    ->SetWebGPUExecutionContextToken(
-                        GetExecutionContextToken(execution_context));
+    if (!context_provider) {
+      // Failed to create context provider, won't be able to request adapter
+      // TODO(crbug.com/973017): Collect GPU info and surface context creation
+      // error.
+      resolver->Resolve(v8::Null(script_state->GetIsolate()));
+      return promise;
+    } else {
+      context_provider->WebGPUInterface()->SetWebGPUExecutionContextToken(
+          GetExecutionContextToken(execution_context));
 
-                // Make a new DawnControlClientHolder with the context provider
-                // we just made and set the lost context callback
-                gpu->dawn_control_client_ = DawnControlClientHolder::Create(
-                    std::move(context_provider),
-                    execution_context->GetTaskRunner(TaskType::kWebGPU));
-              }
-
-              WTF::Vector<base::OnceCallback<void()>> callbacks =
-                  std::move(gpu->dawn_control_client_initialized_callbacks_);
-              for (auto& callback : callbacks) {
-                std::move(callback).Run();
-              }
-            },
-            WrapPersistent(this), WrapPersistent(execution_context)));
-
-    return;
+      // Make a new DawnControlClientHolder with the context provider we just
+      // made and set the lost context callback
+      dawn_control_client_ = DawnControlClientHolder::Create(
+          std::move(context_provider),
+          execution_context->GetTaskRunner(TaskType::kWebGPU));
+    }
   }
 
   DCHECK_NE(dawn_control_client_, nullptr);
@@ -373,13 +332,7 @@
       *execution_context->GetAgent()->event_loop());
 
   UseCounter::Count(execution_context, WebFeature::kWebGPU);
-}
 
-ScriptPromise GPU::requestAdapter(ScriptState* script_state,
-                                  const GPURequestAdapterOptions* options) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise promise = resolver->Promise();
-  RequestAdapterImpl(script_state, options, resolver);
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.h b/third_party/blink/renderer/modules/webgpu/gpu.h
index c699aa0..827d8dc 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu.h
@@ -8,7 +8,6 @@
 #include <dawn/webgpu.h>
 
 #include "base/memory/scoped_refptr.h"
-#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -106,21 +105,7 @@
                                        const GPURequestAdapterOptions* options,
                                        GPUAdapter* adapter) const;
 
-  void OnContextProviderCreated(
-      const KURL& url,
-      ScriptState* script_state,
-      const GPURequestAdapterOptions* options,
-      ScriptPromiseResolver* resolver,
-      ExecutionContext* execution_context,
-      std::unique_ptr<WebGraphicsContext3DProvider> context_provider);
-
-  void RequestAdapterImpl(ScriptState* script_state,
-                          const GPURequestAdapterOptions* options,
-                          ScriptPromiseResolver* resolver);
-
   scoped_refptr<DawnControlClientHolder> dawn_control_client_;
-  WTF::Vector<base::OnceCallback<void()>>
-      dawn_control_client_initialized_callbacks_;
   HeapHashSet<WeakMember<GPUBuffer>> mappable_buffers_;
   // Mappable buffers remove themselves from this set on destruction.
   // It is boxed in a scoped_refptr so GPUBuffer can access it in its
diff --git a/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer.py b/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer.py
index 788a8d4..b607ee2 100644
--- a/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer.py
+++ b/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer.py
@@ -31,6 +31,8 @@
 
 from blinkpy.common.memoized import memoized
 from blinkpy.web_tests.models.testharness_results import is_all_pass_testharness_result
+from blinkpy.web_tests.models.test_expectations import TestExpectations
+from blinkpy.web_tests.models.typ_types import ResultType
 
 _log = logging.getLogger(__name__)
 
@@ -79,40 +81,57 @@
         # For CLI compatibility, "suffix" is an extension without the leading
         # dot. Yet we use dotted extension everywhere else in the codebase.
         # TODO(robertma): Investigate changing the CLI.
+        _log.debug('Optimizing %s(%s).' % (test_name, suffix))
         assert not suffix.startswith('.')
         extension = '.' + suffix
         succeeded = True
 
-        self._optimize_flag_specific_baselines(test_name, extension)
-
-        baseline_name = self._default_port.output_filename(
-            test_name, self._default_port.BASELINE_SUFFIX, extension)
         non_virtual_test_name = self._virtual_test_base(test_name)
         if non_virtual_test_name:
-            # The test belongs to a virtual suite.
-            _log.debug('Optimizing virtual fallback path.')
-            self._patch_virtual_subtree(test_name, extension, baseline_name)
-            succeeded &= self._optimize_subtree(test_name, baseline_name)
-
-            # Update the non-virtual baseline name
+            # Optimize a virtual test and its flag specific versions
             non_virtual_baseline_name = self._default_port.output_filename(
                 non_virtual_test_name, self._default_port.BASELINE_SUFFIX,
                 extension)
-            self._optimize_virtual_root(test_name, extension, baseline_name,
-                                        non_virtual_baseline_name)
+            succeeded = self._optimize_virtual_baseline(
+                test_name, extension, non_virtual_baseline_name)
         else:
-            # The given baseline is already non-virtual.
-            non_virtual_baseline_name = baseline_name
+            # Optimize a real test and all derived virtual/flag specific versions.
+            baseline_name = self._default_port.output_filename(
+                test_name, self._default_port.BASELINE_SUFFIX, extension)
 
-        _log.debug('Optimizing non-virtual fallback path.')
-        succeeded &= self._optimize_subtree(test_name,
-                                            non_virtual_baseline_name)
-        self._remove_extra_result_at_root(test_name, non_virtual_baseline_name)
+            _log.debug('Optimizing virtual fallback paths.')
+            for vts in self._default_port.virtual_test_suites():
+                virtual_test_name = vts.full_prefix + test_name
+                if self._default_port.lookup_virtual_test_base(
+                        virtual_test_name) is None:
+                    # Not a valid virtual test
+                    continue
+                succeeded &= self._optimize_virtual_baseline(
+                    virtual_test_name, extension, baseline_name)
+
+            _log.debug('Optimizing non-virtual fallback path.')
+            succeeded &= self._optimize_subtree(test_name, baseline_name)
+            self._optimize_flag_specific_baselines(test_name, extension)
+            self._remove_extra_result_at_root(test_name, baseline_name)
 
         if not succeeded:
             _log.error('Heuristics failed to optimize %s', baseline_name)
         return succeeded
 
+    def _optimize_virtual_baseline(self, test_name, extension,
+                                   non_virtual_baseline_name):
+        baseline_name = self._default_port.output_filename(
+            test_name, self._default_port.BASELINE_SUFFIX, extension)
+
+        self._patch_virtual_subtree(test_name, extension, baseline_name)
+        succeeded = self._optimize_subtree(test_name, baseline_name)
+
+        self._optimize_virtual_root(test_name, extension, baseline_name,
+                                    non_virtual_baseline_name)
+
+        self._optimize_flag_specific_baselines(test_name, extension)
+        return succeeded
+
     def _optimize_flag_specific_baselines(self, test_name, extension):
         """Optimize flag-specific baselines."""
         for flag_specific in self._flag_specific_configs:
@@ -122,20 +141,8 @@
                                                        flag_specific)
             if not flag_spec_port:
                 continue
-            non_virtual_test_name = self._virtual_test_base(test_name)
-            if non_virtual_test_name:
-                _log.debug(
-                    'Optimizing flag-specific virtual fallback path '
-                    'for "%s".', flag_specific)
-                self._optimize_single_baseline_flag_specific(
-                    test_name, extension, flag_spec_port)
-            else:
-                non_virtual_test_name = test_name
-            _log.debug(
-                'Optimizing flag-specific non-virtual fallback path '
-                'for "%s".', flag_specific)
             self._optimize_single_baseline_flag_specific(
-                non_virtual_test_name, extension, flag_spec_port)
+                test_name, extension, flag_spec_port)
 
     def _get_baseline_paths(self, test_name, extension, port):
         """Get paths to baselines that the provided port would search.
@@ -404,8 +411,16 @@
             test_name, non_virtual_baseline_name)
         results_by_port_name = self._results_by_port_name(results_by_directory)
 
-        for port_name in self._ports.keys():
+        for port_name, port in self._ports.items():
             assert port_name in results_by_port_name
+            # When the virtual test is skipped on a port, the baseline for the
+            # non virtual test on the same port won't matter
+            full_expectations = TestExpectations(port)
+            if ResultType.Skip in full_expectations.get_expectations(
+                    test_name).results:
+                continue
+            if port.skips_test(test_name):
+                continue
             if results_by_port_name[port_name] != virtual_root_digest:
                 return
 
diff --git a/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer_unittest.py b/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer_unittest.py
index 22180d1e..32a3dfc 100644
--- a/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer_unittest.py
+++ b/third_party/blink/tools/blinkpy/common/checkout/baseline_optimizer_unittest.py
@@ -128,6 +128,12 @@
             self.fs.join(web_tests_dir, 'FlagSpecificConfig'),
             '[{"name": "highdpi", "args": ["--force-device-scale-factor=1.5"]}]'
         )
+        self.fs.write_text_file(
+            self.fs.join(web_tests_dir, 'NeverFixTests'),
+            '# tags: [ Linux Mac Mac10.13 Mac10.14 Mac10.15 Mac11 Mac12 Win Win10.20h2 Win11 ]\n'
+            '# results: [ Skip Pass ]\n'
+            '[ Win10.20h2 ] virtual/gpu/fast/canvas/mock-test.html [ Skip ] \n'
+        )
 
         for dirname, contents in results_by_directory.items():
             self.fs.write_text_file(
@@ -355,6 +361,21 @@
                 'platform/mac/fast/canvas': None,
                 'platform/win/fast/canvas': None,
             },
+            baseline_dirname='fast/canvas')
+
+    def test_virtual_root_redundant_with_ancestors_exclude_skipped(self):
+        self._assert_optimization(
+            {
+                'virtual/gpu/fast/canvas': '2',
+                'platform/mac/fast/canvas': '2',
+                'platform/win/fast/canvas': '2',
+                'platform/win10/fast/canvas': '1',
+            }, {
+                'virtual/gpu/fast/canvas': None,
+                'platform/mac/fast/canvas': '2',
+                'platform/win/fast/canvas': '2',
+                'platform/win10/fast/canvas': '1',
+            },
             baseline_dirname='virtual/gpu/fast/canvas')
 
     def test_virtual_root_not_redundant_with_ancestors(self):
@@ -368,6 +389,38 @@
             },
             baseline_dirname='virtual/gpu/fast/canvas')
 
+    def test_virtual_root_not_redundant_with_some_ancestors(self):
+        self._assert_optimization(
+            {
+                'virtual/gpu/fast/canvas': '2',
+                'platform/mac/fast/canvas': '2',
+                'platform/mac-mac11/fast/canvas': '1',
+            }, {
+                'virtual/gpu/fast/canvas': '2',
+                'platform/mac/fast/canvas': '2',
+                'platform/mac-mac11/fast/canvas': '1',
+            },
+            baseline_dirname='virtual/gpu/fast/canvas')
+
+    def test_virtual_root_not_redundant_with_flag_specific_ancestors(self):
+        # virtual root should not be removed when any flag specific non virtual
+        # baseline differs with the virtual root, otherwise virtual flag
+        # specific will fall back to a different baseline after optimization.
+        # TODO: fix this together when we do away with patch virtual subtree.
+        self._assert_optimization(
+            {
+                'virtual/gpu/fast/canvas': '2',
+                'platform/mac/fast/canvas': '2',
+                'platform/win/fast/canvas': '2',
+                'flag-specific/highdpi/fast/canvas': '1',
+            }, {
+                'virtual/gpu/fast/canvas': None,
+                'platform/mac/fast/canvas': '2',
+                'platform/win/fast/canvas': '2',
+                'flag-specific/highdpi/fast/canvas': '1',
+            },
+            baseline_dirname='virtual/gpu/fast/canvas')
+
     def test_virtual_covers_mac_win(self):
         self._assert_optimization(
             {
@@ -578,7 +631,7 @@
                 'platform/linux/fast/canvas': None,
             },
             test_path='fast/canvas',
-            baseline_dirname='virtual/gpu/fast/canvas')
+            baseline_dirname='fast/canvas')
 
     def test_flag_specific_falls_back_to_base(self):
         self._assert_optimization(
@@ -625,7 +678,7 @@
             }, {
                 'fast/canvas': '1',
             },
-            baseline_dirname='virtual/gpu/fast/canvas')
+            baseline_dirname='fast/canvas')
 
     # Tests for protected methods - pylint: disable=protected-access
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index fee0f65..a81ed50 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -129,7 +129,9 @@
             'base::WritableSharedMemoryMapping',
             'base::as_bytes',
             'base::bit_cast',
+            'base::expected',
             'base::make_span',
+            'base::unexpected',
             'base::ranges::.+',
             'base::sequence_manager::TaskTimeObserver',
             'base::span',
@@ -734,6 +736,11 @@
     },
     {
         'paths':
+        ['third_party/blink/renderer/bindings/core/v8/serialization/'],
+        'allowed': ['base::BufferIterator'],
+    },
+    {
+        'paths':
         ['third_party/blink/renderer/bindings/core/v8/script_streamer.cc'],
         'allowed': [
             # For the script streaming to be able to block when reading from a
diff --git a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
index 02cb965..cc154c1 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
@@ -78,18 +78,24 @@
             new_baseline = self._tool.filesystem.join(
                 port.baseline_version_dir(),
                 self._file_name_for_expected_result(test_name, suffix))
-            if self._tool.filesystem.exists(new_baseline):
-                _log.debug('Existing baseline at %s, not copying over it.',
-                           new_baseline)
+
+            if port.skips_test(test_name):
+                self._log_skipped_test(port, test_name)
+                if self._tool.filesystem.exists(new_baseline):
+                    self._tool.filesystem.remove(new_baseline)
                 continue
 
             full_expectations = TestExpectations(port)
             if ResultType.Skip in full_expectations.get_expectations(
                     test_name).results:
                 self._log_skipped_test(port, test_name)
+                # do not delete existing baseline in this case, as this happens
+                # more often, and such baselines could still be in use.
                 continue
-            if port.skips_test(test_name):
-                self._log_skipped_test(port, test_name)
+
+            if self._tool.filesystem.exists(new_baseline):
+                _log.debug('Existing baseline at %s, not copying over it.',
+                           new_baseline)
                 continue
 
             old_baseline = port.expected_filename(test_name, '.' + suffix)
@@ -133,13 +139,20 @@
                 # Not a valid virtual test
                 continue
 
-            if ResultType.Skip in full_expectations.get_expectations(
-                    virtual_test_name).results:
-                self._log_skipped_test(port, virtual_test_name)
-                continue
+            new_baseline = self._tool.filesystem.join(
+                port.baseline_version_dir(),
+                self._file_name_for_expected_result(virtual_test_name, suffix))
 
             if port.skips_test(test_name):
                 self._log_skipped_test(port, virtual_test_name)
+                if self._tool.filesystem.exists(new_baseline):
+                    self._tool.filesystem.remove(new_baseline)
+                continue
+
+            if ResultType.Skip in full_expectations.get_expectations(
+                    virtual_test_name).results:
+                self._log_skipped_test(port, virtual_test_name)
+                # same as above, do not delete existing baselines
                 continue
 
             baseline_dir, filename = port.expected_baselines(
@@ -154,10 +167,6 @@
                 # TODO: downloading all passing baseline for this virtual test harness test
                 continue
 
-            new_baseline = self._tool.filesystem.join(
-                port.baseline_version_dir(),
-                self._file_name_for_expected_result(virtual_test_name, suffix))
-
             _log.debug('Copying baseline from %s to %s.', old_baseline,
                        new_baseline)
             self._tool.filesystem.maybe_make_directory(
diff --git a/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py b/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py
index 3c0f8ff..81e9a5c 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py
@@ -49,6 +49,12 @@
             return
         port = tool.port_factory.get(port_names[0], options)
         optimizer = BaselineOptimizer(tool, port, port_names)
-        tests = port.tests() if options.all_tests else port.tests(args)
-        for test_name in tests:
+        test_set = set(port.tests() if options.all_tests else port.tests(args))
+        virtual_tests_to_exclude = set([
+            test for test in test_set
+            if port.lookup_virtual_test_base(test) in test_set
+        ])
+        test_set -= virtual_tests_to_exclude
+
+        for test_name in test_set:
             self._optimize_baseline(optimizer, test_name)
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
index de6339c6..4d33e377 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -521,6 +521,16 @@
                 continue
             test_set.add(test)
 
+        # For real tests we will optimize all the virtual tests derived from
+        # that. No need to include a virtual tests if we will also optimize the
+        # non virtual version.
+        port = self._tool.port_factory.get()
+        virtual_tests_to_exclude = set([
+            test for test in test_set
+            if port.lookup_virtual_test_base(test) in test_set
+        ])
+        test_set -= virtual_tests_to_exclude
+
         # Process the test_list so that each list caps at MAX_TESTS_IN_OPTIMIZE_CMDLINE tests
         capped_test_list = []
         test_list = list(test_set)
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index 287fe7cf..33bb988 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -1410,6 +1410,16 @@
         return self._filesystem.join(self.web_tests_dir(), 'SmokeTests',
                                      'Default.txt')
 
+    @memoized
+    def _never_fix_test_expectations(self):
+        # Note: The parsing logic here (reading the file, constructing a
+        # parser, etc.) is very similar to blinkpy/w3c/test_copier.py.
+        path = self.path_to_never_fix_tests_file()
+        contents = self._filesystem.read_text_file(path)
+        test_expectations = TestExpectations(tags=self.get_platform_tags())
+        test_expectations.parse_tagged_list(contents)
+        return test_expectations
+
     def skipped_in_never_fix_tests(self, test):
         """Checks if the test is marked as Skip in NeverFixTests for this port.
 
@@ -1420,12 +1430,7 @@
         Note: this will not work with skipped directories. See also the same
         issue with update_all_test_expectations_files in test_importer.py.
         """
-        # Note: The parsing logic here (reading the file, constructing a
-        # parser, etc.) is very similar to blinkpy/w3c/test_copier.py.
-        path = self.path_to_never_fix_tests_file()
-        contents = self._filesystem.read_text_file(path)
-        test_expectations = TestExpectations(tags=self.get_platform_tags())
-        test_expectations.parse_tagged_list(contents)
+        test_expectations = self._never_fix_test_expectations()
         return ResultType.Skip in test_expectations.expectations_for(
             test).results
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 3870d95..c732a3fb 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7244,7 +7244,7 @@
 crbug.com/1363138 http/tests/inspector-protocol/cachestorage/read-cached-response.js [ Skip ]
 
 # Investigate the flake
-crbug.com/1371395 external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js [ Failure Pass ]
+crbug.com/1371395 external/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.html [ Failure Pass ]
 
 # Disable top flakes trigger step failure on builders
 crbug.com/1371949 [ Mac12 ] virtual/low-priority-script-loading/http/tests/devtools/network/resource-priority.js [ Failure Pass ]
@@ -7692,3 +7692,5 @@
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_canplaythrough.html [ Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_loadeddata.html [ Skip ]
 crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_playing.html [ Skip ]
+
+crbug.com/1375928 external/wpt/webrtc-stats/supported-stats.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/crypto/subtle/aes-cbc/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/aes-cbc/cloneKey-expected.txt
index e89b6b8..2e8aec0 100644
--- a/third_party/blink/web_tests/crypto/subtle/aes-cbc/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/aes-cbc/cloneKey-expected.txt
@@ -17,7 +17,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010110031030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010110031030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -36,7 +36,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010120032000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010120032000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -55,7 +55,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010110451030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010110451030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -74,7 +74,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010120452000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010120452000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -93,7 +93,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010110c3011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010110c3011030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -112,7 +112,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010120c3012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010120c3012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -131,7 +131,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010110021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010110021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -149,7 +149,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010120022000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010120022000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -167,7 +167,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010110441030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010110441030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -185,7 +185,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010120442000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010120442000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -203,7 +203,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010110c2011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010110c2011030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -221,7 +221,7 @@
 PASS clonedKey.algorithm.name is "AES-CBC"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010120c2012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010120c2012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/aes-ctr/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/aes-ctr/cloneKey-expected.txt
index 581a002..9126317d 100644
--- a/third_party/blink/web_tests/crypto/subtle/aes-ctr/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/aes-ctr/cloneKey-expected.txt
@@ -17,7 +17,7 @@
 PASS clonedKey.algorithm.name is "AES-CTR"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010b10c1011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010b10c1011030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -36,7 +36,7 @@
 PASS clonedKey.algorithm.name is "AES-CTR"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010b20c1012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010b20c1012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -55,7 +55,7 @@
 PASS clonedKey.algorithm.name is "AES-CTR"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010b10c0011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010b10c0011030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -73,7 +73,7 @@
 PASS clonedKey.algorithm.name is "AES-CTR"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010b20c0012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010b20c0012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/aes-gcm/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/aes-gcm/cloneKey-expected.txt
index ed0bf7e..939a463 100644
--- a/third_party/blink/web_tests/crypto/subtle/aes-gcm/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/aes-gcm/cloneKey-expected.txt
@@ -17,7 +17,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010910031030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010910031030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -36,7 +36,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010920032000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010920032000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -55,7 +55,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010910451030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010910451030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -74,7 +74,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010920452000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010920452000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -93,7 +93,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010910c3011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010910c3011030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -112,7 +112,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010920c3012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010920c3012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -131,7 +131,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010910021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010910021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -149,7 +149,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt"
-Serialized key bytes: 5c4b010920022000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010920022000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -167,7 +167,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010910441030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010910441030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -185,7 +185,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "decrypt,wrapKey"
-Serialized key bytes: 5c4b010920442000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010920442000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -203,7 +203,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010910c2011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010910c2011030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -221,7 +221,7 @@
 PASS clonedKey.algorithm.name is "AES-GCM"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "encrypt,wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010920c2012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010920c2012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/aes-kw/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/aes-kw/cloneKey-expected.txt
index f6d837e..15fc4d4 100644
--- a/third_party/blink/web_tests/crypto/subtle/aes-kw/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/aes-kw/cloneKey-expected.txt
@@ -17,7 +17,7 @@
 PASS clonedKey.algorithm.name is "AES-KW"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010c10c1011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010c10c1011030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -36,7 +36,7 @@
 PASS clonedKey.algorithm.name is "AES-KW"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010c20c1012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010c20c1012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f] and was
 
 
@@ -55,7 +55,7 @@
 PASS clonedKey.algorithm.name is "AES-KW"
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010c10c0011030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b010c10c0011030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -73,7 +73,7 @@
 PASS clonedKey.algorithm.name is "AES-KW"
 PASS clonedKey.algorithm.length is 256
 PASS clonedKey.usages.join(',') is "wrapKey,unwrapKey"
-Serialized key bytes: 5c4b010c20c0012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f
+Serialized key bytes: 5c4b010c20c0012000112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0fa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/ecdh/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/ecdh/cloneKey-expected.txt
index 18b20fed..9ce88fe 100644
--- a/third_party/blink/web_tests/crypto/subtle/ecdh/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/ecdh/cloneKey-expected.txt
@@ -17,7 +17,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050f0101015b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78
+Serialized key bytes: 5c4b050f0101015b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78a0000000014b
 PASS: Cloned key exported data should be [3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78] and was
 
 
@@ -36,7 +36,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050f0101005b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78
+Serialized key bytes: 5c4b050f0101005b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -54,7 +54,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050f010201783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267
+Serialized key bytes: 5c4b050f010201783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267a0000000014b
 PASS: Cloned key exported data should be [3076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267] and was
 
 
@@ -73,7 +73,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050f010200783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267
+Serialized key bytes: 5c4b050f010200783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -91,7 +91,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050f0103019e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee
+Serialized key bytes: 5c4b050f0103019e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71deea0000000014b
 PASS: Cloned key exported data should be [30819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee] and was
 
 
@@ -110,7 +110,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050f0103009e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee
+Serialized key bytes: 5c4b050f0103009e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71deea0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -128,7 +128,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b050f020181028a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6
+Serialized key bytes: 5c4b050f020181028a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6a0000000014b
 PASS: Cloned key exported data should be [308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6] and was
 
 
@@ -147,7 +147,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b050f020180028a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6
+Serialized key bytes: 5c4b050f020180028a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -165,7 +165,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b050f02028102b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983e
+Serialized key bytes: 5c4b050f02028102b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983ea0000000014b
 PASS: Cloned key exported data should be [3081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983e] and was
 
 
@@ -184,7 +184,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b050f02028002b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983e
+Serialized key bytes: 5c4b050f02028002b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983ea0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -202,7 +202,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b050f02038102f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64d
+Serialized key bytes: 5c4b050f02038102f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64da0000000014b
 PASS: Cloned key exported data should be [3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64d] and was
 
 
@@ -221,7 +221,7 @@
 PASS clonedKey.algorithm.name is "ECDH"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b050f02038002f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64d
+Serialized key bytes: 5c4b050f02038002f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64da0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/ecdsa/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/ecdsa/cloneKey-expected.txt
index 7b979a9..2a971448 100644
--- a/third_party/blink/web_tests/crypto/subtle/ecdsa/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/ecdsa/cloneKey-expected.txt
@@ -17,7 +17,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050e0101015b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78
+Serialized key bytes: 5c4b050e0101015b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78a0000000014b
 PASS: Cloned key exported data should be [3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78] and was
 
 
@@ -36,7 +36,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b050e0101115b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78
+Serialized key bytes: 5c4b050e0101115b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78a0000000014b
 PASS: Cloned key exported data should be [3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78] and was
 
 
@@ -55,7 +55,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050e0101005b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78
+Serialized key bytes: 5c4b050e0101005b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -73,7 +73,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b050e0101105b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78
+Serialized key bytes: 5c4b050e0101105b3059301306072a8648ce3d020106082a8648ce3d030107034200049cb0cf69303dafc761d4e4687b4ecf039e6d34ab964af80810d8d558a4a8d6f72d51233a1788920a86ee08a1962c79efa317fb7879e297dad2146db995fa1c78a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -91,7 +91,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050e010201783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267
+Serialized key bytes: 5c4b050e010201783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267a0000000014b
 PASS: Cloned key exported data should be [3076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267] and was
 
 
@@ -110,7 +110,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b050e010211783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267
+Serialized key bytes: 5c4b050e010211783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267a0000000014b
 PASS: Cloned key exported data should be [3076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267] and was
 
 
@@ -129,7 +129,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050e010200783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267
+Serialized key bytes: 5c4b050e010200783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -147,7 +147,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b050e010210783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267
+Serialized key bytes: 5c4b050e010210783076301006072a8648ce3d020106052b81040022036200040874a2e0b8ff448f0e54321e27f4f1e64d064cdeb7d26f458c32e930120f4e57dc85c2693f977eed4a8ecc8db981b4d91f69446df4f4c6f5de19003f45f891d0ebcd2fffdb5c81c040e8d6994c43c7feedb98a4a31edfb35e89a30013c3b9267a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -165,7 +165,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050e0103019e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee
+Serialized key bytes: 5c4b050e0103019e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71deea0000000014b
 PASS: Cloned key exported data should be [30819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee] and was
 
 
@@ -184,7 +184,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b050e0103119e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee
+Serialized key bytes: 5c4b050e0103119e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71deea0000000014b
 PASS: Cloned key exported data should be [30819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee] and was
 
 
@@ -203,7 +203,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b050e0103009e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee
+Serialized key bytes: 5c4b050e0103009e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71deea0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -221,7 +221,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b050e0103109e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71dee
+Serialized key bytes: 5c4b050e0103109e0130819b301006072a8648ce3d020106052b81040023038186000400f50a08703250c15f043c8c46e99783435245cf98f4f2694b0e2f8d029a514dd6f0b086d4ed892000cd5590107aae69c4c0a7a95f7cf74e5770a07d5db55bce4ab400f2c770bab8b9be4cdb6ecd3dc26c698da0d2599cebf3d904f7f9ca3a55e64731810d73cd317264e50baba4bc2860857e16d6cbb79501bc9e3a32bd172ea8a71deea0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -239,7 +239,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b050e0201098a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6
+Serialized key bytes: 5c4b050e0201098a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6a0000000014b
 PASS: Cloned key exported data should be [308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6] and was
 
 
@@ -258,7 +258,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b050e0201088a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6
+Serialized key bytes: 5c4b050e0201088a01308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104201fe33950c5f461124ae992c2bdfdf1c73b1615f571bd567e60d19aa1f48cdf42a144034200047c110c66dcfda807f6e69e45ddb3c74f69a1484d203e8dc5ada8e9a9dd7cb3c70df448986e51bde5d1576f99901f9c2c6a806a47fd907643a72b835597efc8c6a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -276,7 +276,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b050e020209b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983e
+Serialized key bytes: 5c4b050e020209b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983ea0000000014b
 PASS: Cloned key exported data should be [3081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983e] and was
 
 
@@ -295,7 +295,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-384"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b050e020208b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983e
+Serialized key bytes: 5c4b050e020208b9013081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a492ce8fa90084c227e1a32f7974d39e9ff67a7e8705ec3419b35fb607582bebd461e0b1520ac76ec2dd4e9b63ebae71a16403620004e55fee6c49d8d523f5ce7bf9c0425ce4ff650708b7de5cfb095901523979a7f042602db30854735369813b5c3f5ef86828f59cc5dc509892a988d38a8e2519de3d0c4fd0fbdb0993e38f18506c17606c5e24249246f1ce94983a5361c5be983ea0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -313,7 +313,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b050e020309f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64d
+Serialized key bytes: 5c4b050e020309f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64da0000000014b
 PASS: Cloned key exported data should be [3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64d] and was
 
 
@@ -332,7 +332,7 @@
 PASS clonedKey.algorithm.name is "ECDSA"
 PASS clonedKey.algorithm.namedCurve is "P-521"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b050e020308f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64d
+Serialized key bytes: 5c4b050e020308f1013081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044201bd56bd106118eda246155bd43b42b8e13f0a6e25dd3bb376026fab4dc92b6157bc6dfec2d15dd3d0cf2a39aa68494042af48ba9601118da82c6f2108a3a203ad74a181890381860004012fbcaeffa6a51f3ee4d3d2b51c5dec6d7c726ca353fc014ea2bf7cfbb9b910d32cbfa6a00fe39b6cdb8946f22775398b2e233c0cf144d78c8a7742b5c7a3bb5d23009cdef823dd7bf9a79e8cceacd2e4527c231d0ae5967af0958e931d7ddccf2805a3e618dc3039fec9febbd33052fe4c0fee98f033106064982d88f4e03549d4a64da0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/hkdf/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/hkdf/cloneKey-expected.txt
index 657a4052e..1b533f9 100644
--- a/third_party/blink/web_tests/crypto/subtle/hkdf/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/hkdf/cloneKey-expected.txt
@@ -15,7 +15,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b0610800200
+Serialized key bytes: 5c4b0610800200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -31,7 +31,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080020130
+Serialized key bytes: 5c4b061080020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -47,7 +47,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b06108002080011223344554677
+Serialized key bytes: 5c4b06108002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -63,7 +63,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080020b00112233445546778899aa
+Serialized key bytes: 5c4b061080020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -79,7 +79,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b061080021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -95,7 +95,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b06102000
+Serialized key bytes: 5c4b06102000a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -111,7 +111,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610200130
+Serialized key bytes: 5c4b0610200130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -127,7 +127,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b061020080011223344554677
+Serialized key bytes: 5c4b061020080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -143,7 +143,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610200b00112233445546778899aa
+Serialized key bytes: 5c4b0610200b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -159,7 +159,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610201030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0610201030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -175,7 +175,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a00200
+Serialized key bytes: 5c4b0610a00200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -191,7 +191,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0020130
+Serialized key bytes: 5c4b0610a0020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -207,7 +207,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a002080011223344554677
+Serialized key bytes: 5c4b0610a002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -223,7 +223,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0020b00112233445546778899aa
+Serialized key bytes: 5c4b0610a0020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -239,7 +239,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0610a0021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -255,7 +255,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b0610800200
+Serialized key bytes: 5c4b0610800200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -271,7 +271,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080020130
+Serialized key bytes: 5c4b061080020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -287,7 +287,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b06108002080011223344554677
+Serialized key bytes: 5c4b06108002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -303,7 +303,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080020b00112233445546778899aa
+Serialized key bytes: 5c4b061080020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -319,7 +319,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b061080021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -335,7 +335,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b06102000
+Serialized key bytes: 5c4b06102000a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -351,7 +351,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610200130
+Serialized key bytes: 5c4b0610200130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -367,7 +367,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b061020080011223344554677
+Serialized key bytes: 5c4b061020080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -383,7 +383,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610200b00112233445546778899aa
+Serialized key bytes: 5c4b0610200b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -399,7 +399,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610201030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0610201030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -415,7 +415,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a00200
+Serialized key bytes: 5c4b0610a00200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -431,7 +431,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0020130
+Serialized key bytes: 5c4b0610a0020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -447,7 +447,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a002080011223344554677
+Serialized key bytes: 5c4b0610a002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -463,7 +463,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0020b00112233445546778899aa
+Serialized key bytes: 5c4b0610a0020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -479,7 +479,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0610a0021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -495,7 +495,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b0610800200
+Serialized key bytes: 5c4b0610800200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -511,7 +511,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080020130
+Serialized key bytes: 5c4b061080020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -527,7 +527,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b06108002080011223344554677
+Serialized key bytes: 5c4b06108002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -543,7 +543,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080020b00112233445546778899aa
+Serialized key bytes: 5c4b061080020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -559,7 +559,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061080021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b061080021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -575,7 +575,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b06102000
+Serialized key bytes: 5c4b06102000a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -591,7 +591,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610200130
+Serialized key bytes: 5c4b0610200130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -607,7 +607,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b061020080011223344554677
+Serialized key bytes: 5c4b061020080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -623,7 +623,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610200b00112233445546778899aa
+Serialized key bytes: 5c4b0610200b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -639,7 +639,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0610201030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0610201030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -655,7 +655,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a00200
+Serialized key bytes: 5c4b0610a00200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -671,7 +671,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0020130
+Serialized key bytes: 5c4b0610a0020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -687,7 +687,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a002080011223344554677
+Serialized key bytes: 5c4b0610a002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -703,7 +703,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0020b00112233445546778899aa
+Serialized key bytes: 5c4b0610a0020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -719,7 +719,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "HKDF"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0610a0021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0610a0021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/hmac/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/hmac/cloneKey-expected.txt
index bfe5cd3..a7874cf 100644
--- a/third_party/blink/web_tests/crypto/subtle/hmac/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/hmac/cloneKey-expected.txt
@@ -19,7 +19,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020105090130
+Serialized key bytes: 5c4b020105090130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -40,7 +40,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b02080509080011223344554677
+Serialized key bytes: 5c4b02080509080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -61,7 +61,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020b05090b00112233445546778899aa
+Serialized key bytes: 5c4b020b05090b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -82,7 +82,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b021005091030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021005091030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -103,7 +103,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020105110130
+Serialized key bytes: 5c4b020105110130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -124,7 +124,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b02080511080011223344554677
+Serialized key bytes: 5c4b02080511080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -145,7 +145,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020b05110b00112233445546778899aa
+Serialized key bytes: 5c4b020b05110b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -166,7 +166,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b021005111030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021005111030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -187,7 +187,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020105190130
+Serialized key bytes: 5c4b020105190130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -208,7 +208,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b02080519080011223344554677
+Serialized key bytes: 5c4b02080519080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -229,7 +229,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020b05190b00112233445546778899aa
+Serialized key bytes: 5c4b020b05190b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -250,7 +250,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b021005191030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021005191030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -271,7 +271,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020105080130
+Serialized key bytes: 5c4b020105080130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -291,7 +291,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b02080508080011223344554677
+Serialized key bytes: 5c4b02080508080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -311,7 +311,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020b05080b00112233445546778899aa
+Serialized key bytes: 5c4b020b05080b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -331,7 +331,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b021005081030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021005081030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -351,7 +351,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020105100130
+Serialized key bytes: 5c4b020105100130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -371,7 +371,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b02080510080011223344554677
+Serialized key bytes: 5c4b02080510080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -391,7 +391,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020b05100b00112233445546778899aa
+Serialized key bytes: 5c4b020b05100b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -411,7 +411,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b021005101030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021005101030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -431,7 +431,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020105180130
+Serialized key bytes: 5c4b020105180130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -451,7 +451,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b02080518080011223344554677
+Serialized key bytes: 5c4b02080518080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -471,7 +471,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020b05180b00112233445546778899aa
+Serialized key bytes: 5c4b020b05180b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -491,7 +491,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b021005181030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021005181030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -511,7 +511,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020106090130
+Serialized key bytes: 5c4b020106090130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -532,7 +532,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b02080609080011223344554677
+Serialized key bytes: 5c4b02080609080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -553,7 +553,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020b06090b00112233445546778899aa
+Serialized key bytes: 5c4b020b06090b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -574,7 +574,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b021006091030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021006091030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -595,7 +595,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020106110130
+Serialized key bytes: 5c4b020106110130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -616,7 +616,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b02080611080011223344554677
+Serialized key bytes: 5c4b02080611080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -637,7 +637,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020b06110b00112233445546778899aa
+Serialized key bytes: 5c4b020b06110b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -658,7 +658,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b021006111030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021006111030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -679,7 +679,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020106190130
+Serialized key bytes: 5c4b020106190130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -700,7 +700,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b02080619080011223344554677
+Serialized key bytes: 5c4b02080619080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -721,7 +721,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020b06190b00112233445546778899aa
+Serialized key bytes: 5c4b020b06190b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -742,7 +742,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b021006191030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021006191030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -763,7 +763,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020106080130
+Serialized key bytes: 5c4b020106080130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -783,7 +783,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b02080608080011223344554677
+Serialized key bytes: 5c4b02080608080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -803,7 +803,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020b06080b00112233445546778899aa
+Serialized key bytes: 5c4b020b06080b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -823,7 +823,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b021006081030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021006081030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -843,7 +843,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020106100130
+Serialized key bytes: 5c4b020106100130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -863,7 +863,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b02080610080011223344554677
+Serialized key bytes: 5c4b02080610080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -883,7 +883,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020b06100b00112233445546778899aa
+Serialized key bytes: 5c4b020b06100b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -903,7 +903,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b021006101030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021006101030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -923,7 +923,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020106180130
+Serialized key bytes: 5c4b020106180130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -943,7 +943,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b02080618080011223344554677
+Serialized key bytes: 5c4b02080618080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -963,7 +963,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020b06180b00112233445546778899aa
+Serialized key bytes: 5c4b020b06180b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -983,7 +983,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b021006181030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021006181030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1003,7 +1003,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020108090130
+Serialized key bytes: 5c4b020108090130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -1024,7 +1024,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b02080809080011223344554677
+Serialized key bytes: 5c4b02080809080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -1045,7 +1045,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020b08090b00112233445546778899aa
+Serialized key bytes: 5c4b020b08090b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -1066,7 +1066,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b021008091030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021008091030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -1087,7 +1087,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020108110130
+Serialized key bytes: 5c4b020108110130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -1108,7 +1108,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b02080811080011223344554677
+Serialized key bytes: 5c4b02080811080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -1129,7 +1129,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020b08110b00112233445546778899aa
+Serialized key bytes: 5c4b020b08110b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -1150,7 +1150,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b021008111030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021008111030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -1171,7 +1171,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020108190130
+Serialized key bytes: 5c4b020108190130a0000000014b
 PASS: Cloned key exported data should be [30] and was
 
 
@@ -1192,7 +1192,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b02080819080011223344554677
+Serialized key bytes: 5c4b02080819080011223344554677a0000000014b
 PASS: Cloned key exported data should be [0011223344554677] and was
 
 
@@ -1213,7 +1213,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020b08190b00112233445546778899aa
+Serialized key bytes: 5c4b020b08190b00112233445546778899aaa0000000014b
 PASS: Cloned key exported data should be [00112233445546778899aa] and was
 
 
@@ -1234,7 +1234,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b021008191030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021008191030112233445566778899aabbccddeeffa0000000014b
 PASS: Cloned key exported data should be [30112233445566778899aabbccddeeff] and was
 
 
@@ -1255,7 +1255,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020108080130
+Serialized key bytes: 5c4b020108080130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1275,7 +1275,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b02080808080011223344554677
+Serialized key bytes: 5c4b02080808080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1295,7 +1295,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b020b08080b00112233445546778899aa
+Serialized key bytes: 5c4b020b08080b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1315,7 +1315,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b021008081030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021008081030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1335,7 +1335,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020108100130
+Serialized key bytes: 5c4b020108100130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1355,7 +1355,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b02080810080011223344554677
+Serialized key bytes: 5c4b02080810080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1375,7 +1375,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b020b08100b00112233445546778899aa
+Serialized key bytes: 5c4b020b08100b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1395,7 +1395,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b021008101030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021008101030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1415,7 +1415,7 @@
 PASS clonedKey.algorithm.length is 8
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020108180130
+Serialized key bytes: 5c4b020108180130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1435,7 +1435,7 @@
 PASS clonedKey.algorithm.length is 64
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b02080818080011223344554677
+Serialized key bytes: 5c4b02080818080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1455,7 +1455,7 @@
 PASS clonedKey.algorithm.length is 88
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b020b08180b00112233445546778899aa
+Serialized key bytes: 5c4b020b08180b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -1475,7 +1475,7 @@
 PASS clonedKey.algorithm.length is 128
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign,verify"
-Serialized key bytes: 5c4b021008181030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b021008181030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/pbkdf2/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/pbkdf2/cloneKey-expected.txt
index 0dc87bb..c83f221 100644
--- a/third_party/blink/web_tests/crypto/subtle/pbkdf2/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/pbkdf2/cloneKey-expected.txt
@@ -15,7 +15,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b0611800200
+Serialized key bytes: 5c4b0611800200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -31,7 +31,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180020130
+Serialized key bytes: 5c4b061180020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -47,7 +47,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b06118002080011223344554677
+Serialized key bytes: 5c4b06118002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -63,7 +63,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180020b00112233445546778899aa
+Serialized key bytes: 5c4b061180020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -79,7 +79,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b061180021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -95,7 +95,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b06112000
+Serialized key bytes: 5c4b06112000a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -111,7 +111,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611200130
+Serialized key bytes: 5c4b0611200130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -127,7 +127,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b061120080011223344554677
+Serialized key bytes: 5c4b061120080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -143,7 +143,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611200b00112233445546778899aa
+Serialized key bytes: 5c4b0611200b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -159,7 +159,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611201030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0611201030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -175,7 +175,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a00200
+Serialized key bytes: 5c4b0611a00200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -191,7 +191,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0020130
+Serialized key bytes: 5c4b0611a0020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -207,7 +207,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a002080011223344554677
+Serialized key bytes: 5c4b0611a002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -223,7 +223,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0020b00112233445546778899aa
+Serialized key bytes: 5c4b0611a0020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -239,7 +239,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0611a0021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -255,7 +255,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b0611800200
+Serialized key bytes: 5c4b0611800200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -271,7 +271,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180020130
+Serialized key bytes: 5c4b061180020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -287,7 +287,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b06118002080011223344554677
+Serialized key bytes: 5c4b06118002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -303,7 +303,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180020b00112233445546778899aa
+Serialized key bytes: 5c4b061180020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -319,7 +319,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b061180021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -335,7 +335,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b06112000
+Serialized key bytes: 5c4b06112000a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -351,7 +351,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611200130
+Serialized key bytes: 5c4b0611200130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -367,7 +367,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b061120080011223344554677
+Serialized key bytes: 5c4b061120080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -383,7 +383,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611200b00112233445546778899aa
+Serialized key bytes: 5c4b0611200b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -399,7 +399,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611201030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0611201030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -415,7 +415,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a00200
+Serialized key bytes: 5c4b0611a00200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -431,7 +431,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0020130
+Serialized key bytes: 5c4b0611a0020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -447,7 +447,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a002080011223344554677
+Serialized key bytes: 5c4b0611a002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -463,7 +463,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0020b00112233445546778899aa
+Serialized key bytes: 5c4b0611a0020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -479,7 +479,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0611a0021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -495,7 +495,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b0611800200
+Serialized key bytes: 5c4b0611800200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -511,7 +511,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180020130
+Serialized key bytes: 5c4b061180020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -527,7 +527,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b06118002080011223344554677
+Serialized key bytes: 5c4b06118002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -543,7 +543,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180020b00112233445546778899aa
+Serialized key bytes: 5c4b061180020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -559,7 +559,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveBits"
-Serialized key bytes: 5c4b061180021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b061180021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -575,7 +575,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b06112000
+Serialized key bytes: 5c4b06112000a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -591,7 +591,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611200130
+Serialized key bytes: 5c4b0611200130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -607,7 +607,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b061120080011223344554677
+Serialized key bytes: 5c4b061120080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -623,7 +623,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611200b00112233445546778899aa
+Serialized key bytes: 5c4b0611200b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -639,7 +639,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey"
-Serialized key bytes: 5c4b0611201030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0611201030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -655,7 +655,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a00200
+Serialized key bytes: 5c4b0611a00200a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -671,7 +671,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0020130
+Serialized key bytes: 5c4b0611a0020130a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -687,7 +687,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a002080011223344554677
+Serialized key bytes: 5c4b0611a002080011223344554677a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -703,7 +703,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0020b00112233445546778899aa
+Serialized key bytes: 5c4b0611a0020b00112233445546778899aaa0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -719,7 +719,7 @@
 PASS clonedKey.extractable is false
 PASS clonedKey.algorithm.name is "PBKDF2"
 PASS clonedKey.usages.join(',') is "deriveKey,deriveBits"
-Serialized key bytes: 5c4b0611a0021030112233445566778899aabbccddeeff
+Serialized key bytes: 5c4b0611a0021030112233445566778899aabbccddeeffa0000000014b
 
 
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/crypto/subtle/resources/common.js b/third_party/blink/web_tests/crypto/subtle/resources/common.js
index afdfa9b..8ff2438 100644
--- a/third_party/blink/web_tests/crypto/subtle/resources/common.js
+++ b/third_party/blink/web_tests/crypto/subtle/resources/common.js
@@ -117,7 +117,7 @@
     if (internals) {
         // Removing the version tag from the output so serialization format changes don't need to update all the crypto tests.
         var serialized = internals.serializeObject(o);
-        var serializedWithoutVersion = new Uint8Array(serialized, 4);
+        var serializedWithoutVersion = new Uint8Array(serialized, 17);
         debug("Serialized key bytes: " + bytesToHexString(serializedWithoutVersion));
     }
 }
diff --git a/third_party/blink/web_tests/crypto/subtle/rsassa-pkcs1-v1_5/cloneKey-expected.txt b/third_party/blink/web_tests/crypto/subtle/rsassa-pkcs1-v1_5/cloneKey-expected.txt
index fb21fa4..5c7543c0 100644
--- a/third_party/blink/web_tests/crypto/subtle/rsassa-pkcs1-v1_5/cloneKey-expected.txt
+++ b/third_party/blink/web_tests/crypto/subtle/rsassa-pkcs1-v1_5/cloneKey-expected.txt
@@ -21,7 +21,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018008030100010501a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010501a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 PASS: Cloned key exported data should be [30819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001] and was
 
 
@@ -44,7 +44,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018010030100010501a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010501a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 PASS: Cloned key exported data should be [30820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001] and was
 
 
@@ -67,7 +67,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018008030100010511a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010511a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 PASS: Cloned key exported data should be [30819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001] and was
 
 
@@ -90,7 +90,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018010030100010511a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010511a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 PASS: Cloned key exported data should be [30820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001] and was
 
 
@@ -113,7 +113,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018008030100010500a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010500a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -135,7 +135,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018010030100010500a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010500a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -157,7 +157,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018008030100010510a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010510a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -179,7 +179,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018010030100010510a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010510a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -201,7 +201,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018008030100010601a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010601a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 PASS: Cloned key exported data should be [30819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001] and was
 
 
@@ -224,7 +224,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018010030100010601a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010601a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 PASS: Cloned key exported data should be [30820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001] and was
 
 
@@ -247,7 +247,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018008030100010611a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010611a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 PASS: Cloned key exported data should be [30819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001] and was
 
 
@@ -270,7 +270,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018010030100010611a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010611a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 PASS: Cloned key exported data should be [30820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001] and was
 
 
@@ -293,7 +293,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018008030100010600a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010600a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -315,7 +315,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018010030100010600a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010600a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -337,7 +337,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018008030100010610a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010610a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -359,7 +359,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018010030100010610a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010610a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -381,7 +381,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018008030100010801a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010801a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 PASS: Cloned key exported data should be [30819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001] and was
 
 
@@ -404,7 +404,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018010030100010801a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010801a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 PASS: Cloned key exported data should be [30820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001] and was
 
 
@@ -427,7 +427,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018008030100010811a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010811a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 PASS: Cloned key exported data should be [30819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001] and was
 
 
@@ -450,7 +450,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018010030100010811a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010811a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 PASS: Cloned key exported data should be [30820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001] and was
 
 
@@ -473,7 +473,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018008030100010800a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010800a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -495,7 +495,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is ""
-Serialized key bytes: 5c4b0403018010030100010800a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010800a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -517,7 +517,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018008030100010810a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001
+Serialized key bytes: 5c4b0403018008030100010810a20130819f300d06092a864886f70d010101050003818d0030818902818100b289c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f017de4232a306a410203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -539,7 +539,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "verify"
-Serialized key bytes: 5c4b0403018010030100010810a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001
+Serialized key bytes: 5c4b0403018010030100010810a60230820122300d06092a864886f70d01010105000382010f003082010a0282010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba967062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c530b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7bd9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f65059ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043dda68881c790f1517671fb7d198fca5ba97bef0203010001a0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -561,7 +561,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028008030100010509f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d
+Serialized key bytes: 5c4b0403028008030100010509f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4da0000000014b
 PASS: Cloned key exported data should be [30820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d] and was
 
 
@@ -584,7 +584,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028010030100010509c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed
+Serialized key bytes: 5c4b0403028010030100010509c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497eda0000000014b
 PASS: Cloned key exported data should be [308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed] and was
 
 
@@ -607,7 +607,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028008030100010508f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d
+Serialized key bytes: 5c4b0403028008030100010508f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4da0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -629,7 +629,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-1"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028010030100010508c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed
+Serialized key bytes: 5c4b0403028010030100010508c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497eda0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -651,7 +651,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028008030100010609f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d
+Serialized key bytes: 5c4b0403028008030100010609f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4da0000000014b
 PASS: Cloned key exported data should be [30820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d] and was
 
 
@@ -674,7 +674,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028010030100010609c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed
+Serialized key bytes: 5c4b0403028010030100010609c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497eda0000000014b
 PASS: Cloned key exported data should be [308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed] and was
 
 
@@ -697,7 +697,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028008030100010608f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d
+Serialized key bytes: 5c4b0403028008030100010608f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4da0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -719,7 +719,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-256"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028010030100010608c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed
+Serialized key bytes: 5c4b0403028010030100010608c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497eda0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -741,7 +741,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028008030100010809f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d
+Serialized key bytes: 5c4b0403028008030100010809f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4da0000000014b
 PASS: Cloned key exported data should be [30820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d] and was
 
 
@@ -764,7 +764,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028010030100010809c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed
+Serialized key bytes: 5c4b0403028010030100010809c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497eda0000000014b
 PASS: Cloned key exported data should be [308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed] and was
 
 
@@ -787,7 +787,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028008030100010808f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d
+Serialized key bytes: 5c4b0403028008030100010808f90430820275020100300d06092a864886f70d01010105000482025f3082025b02010002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4da0000000014b
 
 
 PASS importedKey.extraProperty is "hi"
@@ -809,7 +809,7 @@
 PASS: clonedKey.algorithm.publicExponent should be [010001] and was
 PASS clonedKey.algorithm.hash.name is "SHA-512"
 PASS clonedKey.usages.join(',') is "sign"
-Serialized key bytes: 5c4b0403028010030100010808c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497ed
+Serialized key bytes: 5c4b0403028010030100010808c109308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100e085e33c42d3f3d63434e0bf1b812c444e790ff6c0becf2cc9de895afa601457bd0fafa81e2b977ab818d086d018491d7ff45be40e916c4c81b3d055ac1803b514e1010b983ff072b77fa228dd47024e65a3c72ab4e2ed02901ba43351dcd87beef92036983c42495d9efec5b4ea1113848b53287099cff1ba58a7829af733f19269101da8c10aa1d5d6b34d381dbb0704e58904c3504a1922f6956e95596780a5db08fe944ff3056ec070eafa66f8c1c5d7dcde4ce2c2694f240aec528eb699089e29f669fb3405c47d6309a609936e8b64e003b5f40743be6b7c2d5673e305cc4e2d154b2a046fba889b60913271b00d5321fd15f82cc41b2894ffc2e54d0b0203010001028201007ae930b59b8bf66f6c130a79f42fa9b117187521caf069f005eeda58c0b9fa48f1c9f58a5e41d4e22c880117dc317f4d33efeca2134b8ef2ef0a25e1d09d30e25fb4b162cc8d2c2f50bf0161c78908fd2bed15aa0e6e2ffb78327998529748b7c7e1ffbd8367718e423f390fb8736eb7b596a4067e65e58d5a4b1020927f03a293d5d3b32931d2a06542e9bbeab7e90085afc9fe145d1c28f44a97f8d9eb8c41913ed9f40261ac5f86806254e26aa8502b92779794a7b08ea299865cbdfeab4dd16307169902b46dbe132b035f7e2742f779f5b39a0b40099434a8af14c78f705abaa7474633a7c6306b2673a97aae35b6a3bf6a5fdd9a6b8d17bd6bbfa1872902818100f2bdbf99a43a4064ac5c85ccff42cccf14fa35250a8d77db6746478d0aea456377dc304688a55b1d2b14c46189ff4f92b078902ff66d144116038667cbbf5bfe80a775aa6eb9885686193debf030eaba00a8cbf26a4fdce1cb433a3a0ccd9696d64448141b0c5d9dfb32bdc29a271594d8e2bc0639daeec705b2cc12419876b702818100ecc964d844657b98dc6b3c6df58a4b5adff0cf5ac1a68679de2baccb4cda1b767f879e441769e5cc0065d8e573f9d728bbd4e25553ee8a2ba779eb219ad0a108daea34e7054d1767e644bdb63880234ac8143a9dda28d3f9196508088e2722ab86a3aa10e599b3caf226e69bee90a1ae7f289b945a34a4fed5e34f9d216b284d028180150ba27afda4174523347a5d459c5309793620396feac8037bb6ba295e52e565345520d25cb2896dc3f86ef64df296c18f0f44e103aa7d610f398b03a0c49c833a404a91563c3bb7d4b4878bd72d468c8dd614a895d30ac180cff952631dc7fa97e51fa2ae9da9d83299399e8fa2e7da19dbbe95839a99ad23af56c6166dd38d02818005da6497c3f90e391519c180a65528cfb2416d9ebcb2b5184619a647d03a83fb45e3c051c692638fcb62b91dd2e4162177a327851c720510572f7854785337e7d4217df547f843dfd99d516333ba5724fe1521edccfabd62a6f20c64c9bec5e89f876428cec421e19e62bfc892f918460bf6a101e5c8ef5b2d46552d792a00f102818100b7d0e59415405a3ce9503e95078078518d381f4273106d67863cfee642d2b82186d9932b7cd921bf1867257473ccd2058d9c88a223701701aa8370b90878df96a6950e3b3ed52326a5cabbf376c97aa644c50fafe38f7496fb9e69efdd138b3525d8b7ac162bd75fe84cb4ce7066d6b786e80c42d5e63059ae939c7bcfa497eda0000000014b
 
 
 PASS successfullyParsed is true
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 5887d347..09aeb99c 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
@@ -1895,6 +1895,13 @@
         {}
        ]
       ],
+      "break-before-multicol-caption.html": [
+       "ac6f444eaad2e7aa3e11dffd74560928568b5ef9",
+       [
+        null,
+        {}
+       ]
+      ],
       "chrome-bug-1293905.html": [
        "2f1bd84528f5f6da7337f3cdae2ad77520d59a7a",
        [
@@ -105362,7 +105369,7 @@
       ]
      ],
      "align-content-001.htm": [
-      "a4e05e7aedc6730e44b490f42e10ea9fbdf3e520",
+      "5fd60351247f09e991ec5b361635397631ab4616",
       [
        null,
        [
@@ -105382,7 +105389,7 @@
            ],
            [
             0,
-            5709
+            6217
            ]
           ]
          ]
@@ -105391,7 +105398,7 @@
       ]
      ],
      "align-content-002.htm": [
-      "1aca52f3a7f131c4a26a4710b72df5be79877c7a",
+      "09453086142d04aae1c70959720139e1dfecbef1",
       [
        null,
        [
@@ -105411,7 +105418,7 @@
            ],
            [
             0,
-            5708
+            6225
            ]
           ]
          ]
@@ -105420,7 +105427,7 @@
       ]
      ],
      "align-content-003.htm": [
-      "9a750119a625d4317c02fec130ab051a2f5f2671",
+      "df6033bca9e94397971704a4bdc24c4f25b1229c",
       [
        null,
        [
@@ -105440,7 +105447,7 @@
            ],
            [
             0,
-            5708
+            6225
            ]
           ]
          ]
@@ -105449,7 +105456,7 @@
       ]
      ],
      "align-content-004.htm": [
-      "795b00942266e8b936f00cac79ae8d7871fad80f",
+      "4c39bfcc331a4fea0a990c2fafa7426e818d4943",
       [
        null,
        [
@@ -105469,7 +105476,7 @@
            ],
            [
             0,
-            4415
+            4835
            ]
           ]
          ]
@@ -105478,7 +105485,7 @@
       ]
      ],
      "align-content-005.htm": [
-      "c57c6295e53018cf55aaa95cfb4749a70bc12348",
+      "a9b70b742bf81181b7fd52531d4e79dfa11eb8df",
       [
        null,
        [
@@ -105498,7 +105505,7 @@
            ],
            [
             0,
-            6649
+            7236
            ]
           ]
          ]
@@ -105611,7 +105618,7 @@
       ]
      ],
      "align-items-001.htm": [
-      "a66194f678953c5152e7adb9c05a5981ce0022bd",
+      "fcd9205ef41680031fc1b8f5428ac6febaa14393",
       [
        null,
        [
@@ -105631,7 +105638,7 @@
            ],
            [
             0,
-            5709
+            6217
            ]
           ]
          ]
@@ -105640,7 +105647,7 @@
       ]
      ],
      "align-items-002.htm": [
-      "f6e1a2193e49b8b6b23fd12fd300b96f0e2d7fd7",
+      "751f803325f09a850538fa6a2d75c7e3ad7e2d03",
       [
        null,
        [
@@ -105660,7 +105667,7 @@
            ],
            [
             0,
-            5834
+            6356
            ]
           ]
          ]
@@ -105669,7 +105676,7 @@
       ]
      ],
      "align-items-003.htm": [
-      "b9cbed0f63de7815dad56595342d7520116f05ef",
+      "e2ddd3c33449664ee397abcc7925119f2559cedf",
       [
        null,
        [
@@ -105689,7 +105696,7 @@
            ],
            [
             0,
-            5820
+            6347
            ]
           ]
          ]
@@ -105698,7 +105705,7 @@
       ]
      ],
      "align-items-004.htm": [
-      "8552847703b3cbce22f1d8365e6f93f1842d0ffe",
+      "c1464e1e0a000bb7ea27cf634b0c7876cf405978",
       [
        null,
        [
@@ -105718,7 +105725,7 @@
            ],
            [
             0,
-            5868
+            6405
            ]
           ]
          ]
@@ -127090,7 +127097,7 @@
        ]
       ],
       "flex-sizing-rows-indefinite-height.html": [
-       "971599aaf0b25756e006544533119f48fa9a7b64",
+       "6625a321d38af2a5d050009661af7cbdd0ff4ab3",
        [
         null,
         [
@@ -127110,7 +127117,7 @@
             ],
             [
              0,
-             3958
+             4309
             ]
            ]
           ]
@@ -127340,7 +127347,7 @@
        ]
       ],
       "grid-template-flexible-rerun-track-sizing.html": [
-       "3e5fc4593bc634ea82ff3387380230977a217d1f",
+       "2db9d643478ff0737becf3a1a4b39705d73ae2b4",
        [
         null,
         [
@@ -127360,7 +127367,7 @@
             ],
             [
              0,
-             3958
+             4309
             ]
            ]
           ]
@@ -270620,23 +270627,19 @@
        []
       ],
       "color-computed-color-mix-function-expected.txt": [
-       "78784ba8f0b7df1d8d5f3cf2baae1e09bc2d453c",
+       "f8f529272556192de12b5c51c3070e4b88484819",
        []
       ],
       "color-computed-relative-color-expected.txt": [
        "be289abfb3387cff3a93d7db69c738910fcc6a3f",
        []
       ],
-      "color-mix-computed-expected.txt": [
-       "78784ba8f0b7df1d8d5f3cf2baae1e09bc2d453c",
-       []
-      ],
-      "color-mix-valid-expected.txt": [
-       "7116c476434c367b981875c49d710341feb20110",
+      "color-invalid-color-mix-function-expected.txt": [
+       "5635c3e0ba1c0460bd072df9047c8ab71da59e79",
        []
       ],
       "color-valid-color-mix-function-expected.txt": [
-       "7116c476434c367b981875c49d710341feb20110",
+       "932d4828f0709e6d13d0251c810f50a5c542c1bd",
        []
       ],
       "color-valid-relative-color-expected.txt": [
@@ -270646,18 +270649,6 @@
       "color-valid-system-color-expected.txt": [
        "0ab1033c4e9a82659ec136f2084d572ab442298d",
        []
-      ],
-      "relative-color-computed-expected.txt": [
-       "be289abfb3387cff3a93d7db69c738910fcc6a3f",
-       []
-      ],
-      "relative-color-valid-expected.txt": [
-       "5bf4e6a04c36ce0bdc421bb588d5e07dd14b92a4",
-       []
-      ],
-      "system-color-valid-expected.txt": [
-       "0ab1033c4e9a82659ec136f2084d572ab442298d",
-       []
       ]
      },
      "prophoto-rgb-003-ref.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-001.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-001.htm
index a4e05e7a..5fd6035 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-001.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-001.htm
@@ -7,7 +7,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that a multi-line flex container with 'align-content: center' centers lines in the flex container." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5709" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6217" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-002.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-002.htm
index 1aca52f..0945308 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-002.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-002.htm
@@ -6,7 +6,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that a multi-line flex container with 'align-content: flex-start' packs lines toward the start of the flex container." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5708" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6225" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-003.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-003.htm
index 9a75011..df6033bc 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-003.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-003.htm
@@ -6,7 +6,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that a multi-line flex container with 'align-content: flex-end' packs lines toward the end of the flex container." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5708" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6225" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-004.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-004.htm
index 795b009..4c39bfc 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-004.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-004.htm
@@ -6,7 +6,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that a multi-line flex container with 'align-content: space-between' distributes lines evenly in the flex container." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-4415" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-4835" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-005.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-005.htm
index c57c6295..a9b70b74 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-005.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-content-005.htm
@@ -6,7 +6,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that a multi-line flex container with 'align-content: space-around' distributes lines evenly in the flex container, with half-size spaces on either end." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6649" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-7236" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-001.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-001.htm
index a66194f..fcd9205 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-001.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-001.htm
@@ -7,7 +7,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that the flex container with 'align-items: center' centers each flex item's margin box in the cross-axis of its line." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5709" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6217" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-002.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-002.htm
index f6e1a21..751f803 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-002.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-002.htm
@@ -7,7 +7,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that the flex container with 'align-items: flex-start' places each flex item's margin box flush with the cross-start edge of line." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5834" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6356" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-003.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-003.htm
index b9cbed0..e2ddd3c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-003.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-003.htm
@@ -7,7 +7,7 @@
         <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property" />
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="assert" content="This test checks that the flex container with 'align-items: flex-end' places each flex item's margin box flush with the cross-end edge of line." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5820" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6347" />
         <style type="text/css">
             #flexbox
             {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-004.htm b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-004.htm
index 8552847..c1464e1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-004.htm
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-004.htm
@@ -8,7 +8,7 @@
         <link rel="match" href="reference/align-content-001-ref.html" />
         <meta name="flags" content="ahem">
         <meta name="assert" content="This test checks that the flex container with 'align-items: baseline' places each flex item's margin box so that their baselines align." />
-        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-5868" />
+        <meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-6405" />
         <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
         <style type="text/css">
             #flexbox
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/flex-sizing-rows-indefinite-height.html b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/flex-sizing-rows-indefinite-height.html
index 971599a..6625a32 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/flex-sizing-rows-indefinite-height.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/flex-sizing-rows-indefinite-height.html
@@ -2,7 +2,7 @@
 <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1149627">
 <link rel="help" href="https://drafts.csswg.org/css-grid/#algo-flex-tracks">
 <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
-<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-3958">
+<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-4309" />
 <style>
 .grid {
   display: inline-grid;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-template-flexible-rerun-track-sizing.html b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-template-flexible-rerun-track-sizing.html
index 3e5fc45..2db9d64 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-template-flexible-rerun-track-sizing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-template-flexible-rerun-track-sizing.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1149627">
 <link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
-<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-3958">
+<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-4309" />
 <style>
 .grid {
   display: inline-grid;
diff --git a/third_party/blink/web_tests/external/wpt/mimesniff/mime-types/charset-parameter.window.js b/third_party/blink/web_tests/external/wpt/mimesniff/mime-types/charset-parameter.window.js
index 5dc46562..923c3a3d 100644
--- a/third_party/blink/web_tests/external/wpt/mimesniff/mime-types/charset-parameter.window.js
+++ b/third_party/blink/web_tests/external/wpt/mimesniff/mime-types/charset-parameter.window.js
@@ -1,3 +1,5 @@
+// META: timeout=long
+
 promise_test(() => {
   // Don't load generated-mime-types.json as none of them are navigable
   return fetch("resources/mime-types.json").then(res => res.json().then(runTests));
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/postMessage_CryptoKey_insecure.sub.html b/third_party/blink/web_tests/external/wpt/webmessaging/postMessage_CryptoKey_insecure.sub.html
new file mode 100644
index 0000000..8e5bdcb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/postMessage_CryptoKey_insecure.sub.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+  let nextMessageEvent = new Promise((resolve, reject) => {
+    window.addEventListener('message', e => resolve(e));
+    window.addEventListener('messageerror', e => resolve(e));
+  });
+  let w = window.open('https://{{hosts[][]}}:{{ports[https][0]}}/webmessaging/resources/post-cryptokey-to-opener.html');
+  try {
+    let event = await nextMessageEvent;
+    assert_equals(event.type, 'messageerror');
+  } finally {
+    w.close();
+  }
+}, "insecure context should not receive an object for secure contexts only");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/resources/post-cryptokey-to-opener.html b/third_party/blink/web_tests/external/wpt/webmessaging/resources/post-cryptokey-to-opener.html
new file mode 100644
index 0000000..91c7016
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/resources/post-cryptokey-to-opener.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+crypto.subtle.generateKey({name: 'AES-CBC', length: 128}, false, ['encrypt', 'decrypt'])
+.then(key => opener.postMessage({key}, '*'))
+.catch(error => opener.postMessage({error: error.toString()}, '*'));
+</script>
diff --git a/third_party/blink/web_tests/fast/dom/MutationObserver/document-write.html b/third_party/blink/web_tests/fast/dom/MutationObserver/document-write.html
index 21cfde3..d71db37 100644
--- a/third_party/blink/web_tests/fast/dom/MutationObserver/document-write.html
+++ b/third_party/blink/web_tests/fast/dom/MutationObserver/document-write.html
@@ -1,10 +1,8 @@
 <!DOCTYPE html>
-<head>
-</head>
-<body>
-<div id="result">RESULT</div>
-<script>
 
+<div id="result">RESULT</div>
+
+<script>
 var resultDiv = document.getElementById('result');
 
 if (window.testRunner) {
@@ -13,7 +11,6 @@
 }
 
 var count = 0;
-
 var observer = new MutationObserver(function(r) {
     count += r.length;
 });
@@ -31,23 +28,17 @@
 }
 
 function finish() {
-    setTimeout(function() {
+    requestAnimationFrame(() => requestAnimationFrame(() => {
         check();
-
         var result = counts[0] == 0 &&
                      counts[1] == 0 &&
                      counts[2] == 0 &&
                      counts[3] == 1 ? 'PASSED' : 'FAILED';
-
-        document.documentElement.appendChild(document.createElement('body'));
-        document.body.innerHTML = result;
-        if (window.testRunner) {
+        document.body.appendChild(document.createTextNode(result));
+        if (window.testRunner)
             testRunner.notifyDone();
-        }
-    }, 0);
+    }));
 }
-
-finish();
 </script>
+
 <iframe onload="mutate(); check(); document.write('<script>check();</script>'); document.close(); check(); finish();">
-</body>
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index a340f62..fc4b4dbd 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -864,8 +864,8 @@
  * Gets the list of tasks that can be performed over selected files. |entries|
  * Array of selected entries |callback|
  * @param {!Array<!Entry>} entries
- * @param {function((!Array<!chrome.fileManagerPrivate.FileTask>|undefined))}
- *     callback |tasks| The list of matched file entries for this task.
+ * @param {function((!chrome.fileManagerPrivate.ResultingTasks|undefined))}
+ *     callback The list of matched file tasks for the entries.
  */
 chrome.fileManagerPrivate.getFileTasks = function(entries, callback) {};
 
diff --git a/third_party/instrumented_libraries/focal/scripts/download_build_install.py b/third_party/instrumented_libraries/focal/scripts/download_build_install.py
index ccdab83d..e7421fb2 100755
--- a/third_party/instrumented_libraries/focal/scripts/download_build_install.py
+++ b/third_party/instrumented_libraries/focal/scripts/download_build_install.py
@@ -331,9 +331,9 @@
     files_file = os.path.join(self._source_dir, 'debian/files')
 
     for line in open(files_file, 'r').read().splitlines():
-      if not line.endswith('.deb'):
-        continue
       filename, category, section = line.split(' ')
+      if not filename.endswith('.deb'):
+        continue
       pathname = os.path.join(self._source_dir, '..', filename)
       deb_files.append(pathname)
 
diff --git a/third_party/omnibox_proto/README.chromium b/third_party/omnibox_proto/README.chromium
index 6987ce4c..811676d 100644
--- a/third_party/omnibox_proto/README.chromium
+++ b/third_party/omnibox_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Omnibox Protos
 Short Name: omnibox_proto
 URL: This is the canonical public repository
-Version: 480506726
-Date: 2022/10/12 UTC
+Version: 481038145
+Date: 2022/10/14 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/omnibox_proto/groups.proto b/third_party/omnibox_proto/groups.proto
index 0d6abf7a..15ea370 100644
--- a/third_party/omnibox_proto/groups.proto
+++ b/third_party/omnibox_proto/groups.proto
@@ -118,10 +118,9 @@
   // - Organic Repeatable Queries
   SECTION_MOBILE_MOST_VISITED = 3;
 
-  // Local history zero-prefix suggestions. Will be overwritten by the
-  // dynamically assigned section for GROUP_PERSONALIZED_ZERO_SUGGEST if found
-  // in the server response.
-  SECTION_LOCAL_HISTORY_ZPS = 4;
+  // historical zero-prefix suggestions. May be overwritten by the dynamically
+  // assigned section for GROUP_PERSONALIZED_ZERO_SUGGEST if reported by server.
+  SECTION_PERSONALIZED_ZERO_SUGGEST = 4;
 
   // A contiguous range reserved for remote zero-prefix suggestions.
   // The sections are dynamically assigned to the groups found in the server
diff --git a/tools/diagnose-me.py b/tools/diagnose-me.py
index 51bd60357..3f8a060 100755
--- a/tools/diagnose-me.py
+++ b/tools/diagnose-me.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2012 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -15,97 +15,98 @@
 all_checks = []
 
 def Check(name):
-    """Decorator that defines a diagnostic check."""
-    def wrap(func):
-        all_checks.append((name, func))
-        return func
-    return wrap
+  """Decorator that defines a diagnostic check."""
+
+  def wrap(func):
+    all_checks.append((name, func))
+    return func
+
+  return wrap
 
 
 @Check("/usr/bin/ld is not gold")
 def CheckSystemLd():
-    proc = subprocess.Popen(['/usr/bin/ld', '-v'], stdout=subprocess.PIPE)
-    stdout = proc.communicate()[0]
-    if 'GNU gold' in stdout:
-        return ("When /usr/bin/ld is gold, system updates can silently\n"
-                "corrupt your graphics drivers.\n"
-                "Try 'sudo apt-get remove binutils-gold'.\n")
-    return None
+  proc = subprocess.Popen(['/usr/bin/ld', '-v'], stdout=subprocess.PIPE)
+  stdout = proc.communicate()[0].decode('utf-8')
+  if 'GNU gold' in stdout:
+    return ("When /usr/bin/ld is gold, system updates can silently\n"
+            "corrupt your graphics drivers.\n"
+            "Try 'sudo apt-get remove binutils-gold'.\n")
+  return None
 
 
 @Check("random lds are not in the $PATH")
 def CheckPathLd():
-    proc = subprocess.Popen(['which', '-a', 'ld'], stdout=subprocess.PIPE)
-    stdout = proc.communicate()[0]
-    instances = stdout.split()
-    if len(instances) > 1:
-        return ("You have multiple 'ld' binaries in your $PATH:\n"
-                + '\n'.join(' - ' + i for i in instances) + "\n"
-                "You should delete all of them but your system one.\n"
-                "gold is hooked into your build via gyp.\n")
-    return None
+  proc = subprocess.Popen(['which', '-a', 'ld'], stdout=subprocess.PIPE)
+  stdout = proc.communicate()[0].decode('utf-8')
+  instances = stdout.split()
+  if len(instances) > 1:
+    return ("You have multiple 'ld' binaries in your $PATH:\n" +
+            '\n'.join(' - ' + i for i in instances) + "\n"
+            "You should delete all of them but your system one.\n"
+            "gold is hooked into your build via depot tools.\n")
+  return None
 
 
 @Check("/usr/bin/ld doesn't point to gold")
 def CheckLocalGold():
-    # Check /usr/bin/ld* symlinks.
-    for path in ('ld.bfd', 'ld'):
-        path = '/usr/bin/' + path
-        try:
-            target = os.readlink(path)
-        except OSError, e:
-            if e.errno == 2:
-                continue  # No such file
-            if e.errno == 22:
-                continue  # Not a symlink
-            raise
-        if '/usr/local/gold' in target:
-            return ("%s is a symlink into /usr/local/gold.\n"
-                    "It's difficult to make a recommendation, because you\n"
-                    "probably set this up yourself.  But you should make\n"
-                    "/usr/bin/ld be the standard linker, which you likely\n"
-                    "renamed /usr/bin/ld.bfd or something like that.\n" % path)
+  # Check /usr/bin/ld* symlinks.
+  for path in ('ld.bfd', 'ld'):
+    path = '/usr/bin/' + path
+    try:
+      target = os.readlink(path)
+    except OSError as e:
+      if e.errno == 2:
+        continue  # No such file
+      if e.errno == 22:
+        continue  # Not a symlink
+      raise
+    if '/usr/local/gold' in target:
+      return ("%s is a symlink into /usr/local/gold.\n"
+              "It's difficult to make a recommendation, because you\n"
+              "probably set this up yourself.  But you should make\n"
+              "/usr/bin/ld be the standard linker, which you likely\n"
+              "renamed /usr/bin/ld.bfd or something like that.\n" % path)
 
-    return None
+  return None
 
 
 @Check("random ninja binaries are not in the $PATH")
 def CheckPathNinja():
-    proc = subprocess.Popen(['which', 'ninja'], stdout=subprocess.PIPE)
-    stdout = proc.communicate()[0]
-    if not 'depot_tools' in stdout:
-        return ("The ninja binary in your path isn't from depot_tools:\n"
-                + "    " + stdout +
-                "Remove custom ninjas from your path so that the one\n"
-                "in depot_tools is used.\n")
-    return None
+  proc = subprocess.Popen(['which', 'ninja'], stdout=subprocess.PIPE)
+  stdout = proc.communicate()[0].decode('utf-8')
+  if not 'depot_tools' in stdout:
+    return ("The ninja binary in your path isn't from depot_tools:\n" + "    " +
+            stdout + "Remove custom ninjas from your path so that the one\n"
+            "in depot_tools is used.\n")
+  return None
 
 
 @Check("build dependencies are satisfied")
 def CheckBuildDeps():
-    script_path = os.path.join(
-        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'build',
-        'install-build-deps.sh')
-    proc = subprocess.Popen([script_path, '--quick-check'],
-                            stdout=subprocess.PIPE)
-    stdout = proc.communicate()[0]
-    if 'WARNING' in stdout:
-        return ("Your build dependencies are out-of-date.\n"
-                "Run '" + script_path + "' to update.")
-    return None
+  script_path = os.path.join(
+      os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'build',
+      'install-build-deps.sh')
+  proc = subprocess.Popen([script_path, '--quick-check'],
+                          stdout=subprocess.PIPE)
+  stdout = proc.communicate()[0].decode('utf-8')
+  if 'WARNING' in stdout:
+    return ("Your build dependencies are out-of-date.\n"
+            "Run '" + script_path + "' to update.")
+  return None
 
 
 def RunChecks():
-    for name, check in all_checks:
-        sys.stdout.write("* Checking %s: " % name)
-        sys.stdout.flush()
-        error = check()
-        if not error:
-            print("ok")
-        else:
-            print("FAIL")
-            print(error)
+  for name, check in all_checks:
+    sys.stdout.write("* Checking %s: " % name)
+    sys.stdout.flush()
+    error = check()
+    if not error:
+      print("ok")
+    else:
+      print("FAIL")
+      print(error)
 
 
 if __name__ == '__main__':
-    RunChecks()
+  RunChecks()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a5b47c3..31ec2c1 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -42283,6 +42283,9 @@
       label="User selected the 'Group by Publisher' Following feed sort type."/>
   <int value="60"
       label="User selected the 'Sort by Latest' Following feed sort type."/>
+  <int value="61"
+      label="After following an active web feed, the user tapped on 'got it'
+             to close the post-follow help dialog."/>
 </enum>
 
 <enum name="FeedUserCommandType">
@@ -43712,6 +43715,12 @@
   <int value="9" label="Match"/>
   <int value="10" label="ResetSensor"/>
   <int value="11" label="DontChange"/>
+  <int value="12" label="SensorMaintenance"/>
+  <int value="13" label="ModeInvalid">
+    Reported when the get FPMode command fails or other.
+
+    ec::FpMode::Mode::kModeInvalid
+  </int>
 </enum>
 
 <enum name="FingerprintSetupScreenUserAction">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 421b831..2287d08b 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -891,7 +891,7 @@
 
 <histogram
     name="Android.ChildProcessBinding.Percentage{ChildProcessConnectionMetricsBindingState}Connections_{TotalConnectionBucket}"
-    units="%" expires_after="2022-12-01">
+    units="%" expires_after="2023-02-12">
   <owner>ckitagawa@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 95f1efc3..d1d38d2 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -2151,7 +2151,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.AppsProfile.Creation.Duration" units="ms"
-    expires_after="2022-12-01">
+    expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2164,7 +2164,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.AppsProfile.Creation.Success"
-    enum="BooleanSuccess" expires_after="2022-12-01">
+    enum="BooleanSuccess" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2176,7 +2176,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.DataItemStorage.ClearTextItemSize"
-    units="bytes" expires_after="2022-12-01">
+    units="bytes" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2187,7 +2187,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.DataItemStorage.EncryptedItemSize"
-    units="bytes" expires_after="2022-12-01">
+    units="bytes" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2198,7 +2198,7 @@
 
 <histogram
     name="Apps.LockScreen.DataItemStorage.FailedOperationDuration{LockScreenAppDataItemOperation}"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2212,7 +2212,7 @@
 
 <histogram
     name="Apps.LockScreen.DataItemStorage.OperationDuration{LockScreenAppDataItemOperation}"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2227,7 +2227,7 @@
 
 <histogram
     name="Apps.LockScreen.DataItemStorage.OperationResult{LockScreenAppDataItemOperation}"
-    enum="LockScreenDataItemOperationResult" expires_after="2022-12-01">
+    enum="LockScreenDataItemOperationResult" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2239,7 +2239,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.DataItemStorage.RegisteredItemsCount"
-    units="units" expires_after="2022-12-01">
+    units="units" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2250,7 +2250,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.AppStatusOnNoteLaunch"
-    enum="LockScreenNoteAppStatusOnLaunch" expires_after="2022-12-01">
+    enum="LockScreenNoteAppStatusOnLaunch" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2263,7 +2263,7 @@
 
 <histogram
     name="Apps.LockScreen.NoteTakingApp.AppWindowLifeTime{LockScreenNoteTakingAppWindowState}"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2284,7 +2284,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.AvailabilityOnScreenLock"
-    enum="LockScreenActionAvailability" expires_after="2023-02-26">
+    enum="LockScreenActionAvailability" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2294,7 +2294,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.FinalAppSessionState"
-    enum="LockScreenAppSessionState" expires_after="2022-12-01">
+    enum="LockScreenAppSessionState" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2304,7 +2304,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.LaunchDurationAtLaunchCancel"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2315,7 +2315,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.LaunchRequestOrdinalNumber"
-    units="units" expires_after="2022-12-01">
+    units="units" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2327,7 +2327,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.LaunchRequestReason"
-    enum="NewLockScreenNoteRequestType" expires_after="2022-12-01">
+    enum="NewLockScreenNoteRequestType" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2336,7 +2336,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.LockScreenAppUnloaded"
-    enum="LockScreenAppUnloadStatus" expires_after="2022-12-01">
+    enum="LockScreenAppUnloadStatus" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2350,7 +2350,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.LockScreenInstallationDuration"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2360,7 +2360,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.NoteTakingExitReason"
-    enum="LockScreenNoteTakingExitReason" expires_after="2022-12-01">
+    enum="LockScreenNoteTakingExitReason" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2370,7 +2370,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.ReloadCountOnAppTermination"
-    units="units" expires_after="2022-12-01">
+    units="units" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2382,7 +2382,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.TimeToLoadAppWindowContents"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
@@ -2393,7 +2393,7 @@
 </histogram>
 
 <histogram name="Apps.LockScreen.NoteTakingApp.TimeToShowWindow" units="ms"
-    expires_after="2022-12-01">
+    expires_after="2023-06-01">
   <owner>glenrob@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 3d2cacc..68158e8 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -146,7 +146,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.Activation.{TabletOrClamshell}"
-    enum="AmbientUiMode" expires_after="2022-12-01">
+    enum="AmbientUiMode" expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -158,7 +158,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.AnimationSmoothness.PhotoTransition" units="%"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>wutao@chromium.org</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -169,7 +169,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.Enabled" enum="BooleanEnabled"
-    expires_after="2023-04-09">
+    expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -182,7 +182,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.EngagementTime.{TabletOrClamshell}" units="ms"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -194,7 +194,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.EngagementTime.{Theme}" units="ms"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>esum@google.com</owner>
   <summary>
@@ -205,7 +205,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.EngagementTime.{Theme}.{Orientation}"
-    units="ms" expires_after="2022-12-01">
+    units="ms" expires_after="2023-12-01">
   <owner>esum@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -225,7 +225,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.LottieAnimationSmoothness.{Theme}" units="%"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>esum@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -236,7 +236,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.MultiScreenOffset.{Theme}" units="ms"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>esum@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -253,7 +253,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.PhotoOrientationMatch.{Theme}" units="%"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>esum@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -269,7 +269,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.PhotoSource" enum="AmbientModePhotoSource"
-    expires_after="2023-02-12">
+    expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -279,7 +279,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.ScreenCount.{Theme}" units="int"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>esum@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -291,7 +291,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.SelectedNumberOfAlbums" units="int"
-    expires_after="2023-02-19">
+    expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -302,7 +302,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.StartupTime.{Theme}" units="ms"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>esum@google.com</owner>
   <owner>xiaohuic@google.com</owner>
   <summary>
@@ -324,7 +324,7 @@
 </histogram>
 
 <histogram name="Ash.AmbientMode.TotalNumberOfAlbums" units="int"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>cowmoo@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 6d39dfd7..d049b123 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -38,7 +38,7 @@
 </variants>
 
 <histogram name="ExtensionBlacklist.BlacklistInstalled"
-    enum="ExtensionLocation" expires_after="2022-11-18">
+    enum="ExtensionLocation" expires_after="2023-11-18">
   <owner>anunoy@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -49,7 +49,7 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.BlockCRX" enum="ExtensionLocation"
-    expires_after="2022-11-18">
+    expires_after="2023-11-18">
   <owner>anunoy@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -60,7 +60,7 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.SilentInstall" enum="ExtensionLocation"
-    expires_after="2022-11-18">
+    expires_after="2023-11-18">
   <owner>anunoy@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -73,7 +73,7 @@
 </histogram>
 
 <histogram name="ExtensionBlacklist.UnblacklistInstalled"
-    enum="ExtensionLocation" expires_after="2023-03-26">
+    enum="ExtensionLocation" expires_after="2023-11-18">
   <owner>anunoy@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/windows/histograms.xml b/tools/metrics/histograms/metadata/windows/histograms.xml
index 9f719c1f..d821c12f 100644
--- a/tools/metrics/histograms/metadata/windows/histograms.xml
+++ b/tools/metrics/histograms/metadata/windows/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Windows.CetAvailable" enum="BooleanAvailable"
-    expires_after="2022-12-01">
+    expires_after="2023-12-01">
   <owner>ajgo@chromium.org</owner>
   <owner>wfh@chromium.org</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 fef7692b..1dc99ff6 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "81de3e2ff2291a39145a0c358d03905de370dfd8",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/96db79eeca95ba3068331ab1642e757b5879828e/trace_processor_shell.exe"
+            "hash": "26f1b29185b3f770866f885524c168f463eee0dc",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/a77a3622d28b8ae7df746b6591de7b390c3b342a/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "c885cd5b4d630c1eff5d0688fc4631cae1b4e96a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/96db79eeca95ba3068331ab1642e757b5879828e/trace_processor_shell"
+            "hash": "8edab28b6213311ac7e7f85cf02958b6d25ab4de",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/a77a3622d28b8ae7df746b6591de7b390c3b342a/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "92318bea34f5c9beec69d2d826a9a92ec9d3cdcd",
             "full_remote_path": "perfetto-luci-artifacts/v30.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "22917ad477215e268434b73428d8d66a71949182",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/96db79eeca95ba3068331ab1642e757b5879828e/trace_processor_shell"
+            "hash": "d3c03fa3d616e2ca39ad4bc79e753ac3128ab0e9",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/a77a3622d28b8ae7df746b6591de7b390c3b342a/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/compositor/layer_animation_element_unittest.cc b/ui/compositor/layer_animation_element_unittest.cc
index 22a7257..5b56d14c 100644
--- a/ui/compositor/layer_animation_element_unittest.cc
+++ b/ui/compositor/layer_animation_element_unittest.cc
@@ -26,7 +26,7 @@
 TEST(TargetValueTest, VerifyLayerAnimationDelegateConstructor) {
   const gfx::Rect kBounds(1, 2, 3, 5);
   const auto kTransform =
-      gfx::Transform::AffineForTesting(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
+      gfx::Transform::Affine(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
   const float kOpacity = 1.235f;
   const bool kVisibility = false;
   const float kBrightness = 2.358f;
diff --git a/ui/file_manager/file_manager/background/js/file_operation_handler.js b/ui/file_manager/file_manager/background/js/file_operation_handler.js
index f9e9f27..a3c63ee 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_handler.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_handler.js
@@ -117,16 +117,10 @@
         } else {
           item.state = ProgressItemState.ERROR;
         }
-        // Extract IOTask details are only stored while an operation is active.
-        if (item.type == ProgressItemType.EXTRACT) {
-          this.fileOperationManager_.notifyExtractDone(event.taskId);
-        }
         break;
       case chrome.fileManagerPrivate.IOTaskState.NEED_PASSWORD:
         // Set state to canceled so notification doesn't display.
         item.state = ProgressItemState.CANCELED;
-        assert(item.type == ProgressItemType.EXTRACT);
-        this.fileOperationManager_.handleMissingPassword(event.taskId);
         break;
       default:
         console.error(`Invalid IOTaskState: ${event.state}`);
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js
index 01dce20..c402c026 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js
@@ -18,12 +18,6 @@
 export class FileOperationManagerImpl {
   constructor() {
     /**
-     * TODO(crbug.com/953256) Add closure annotation.
-     * @private
-     */
-    this.fileManager_ = null;
-
-    /**
      * @private {VolumeManager}
      */
     this.volumeManager_ = null;
@@ -41,14 +35,6 @@
   }
 
   /**
-   * Store a reference to our owning File Manager.
-   * @param {Object} fileManager reference to the 'foreground' app.
-   */
-  setFileManager(fileManager) {
-    this.fileManager_ = fileManager;
-  }
-
-  /**
    * Filters the entry in the same directory
    *
    * @param {Array<Entry>} sourceEntries Entries of the source files.
@@ -102,37 +88,6 @@
   }
 
   /**
-   * Notifies File Manager that an extraction operation has finished.
-   *
-   * @param {number} taskId The unique task id for the IO operation.
-   * @suppress {missingProperties}
-   */
-  notifyExtractDone(taskId) {
-    // TODO(crbug.com/953256) Add closure annotation.
-    // taskController is set asynchronously, this can be called on startup
-    // if another SWA window is finishing an extract (crbug.com/1348432).
-    if (this.fileManager_.taskController) {
-      this.fileManager_.taskController.deleteExtractTaskDetails(taskId);
-    }
-  }
-
-  /**
-   * Called when an IOTask finished with a NEED_PASSWORD status.
-   * Delegate it to the task controller to deal with it.
-   *
-   * @param {number} taskId The unique task id for the IO operation.
-   * @suppress {missingProperties}
-   */
-  handleMissingPassword(taskId) {
-    // TODO(crbug.com/953256) Add closure annotation.
-    // null check is unlikely to be needed, but there's no guarantee
-    // that taskController has been initialized on a password event.
-    if (this.fileManager_.taskController) {
-      this.fileManager_.taskController.handleMissingPassword(taskId);
-    }
-  }
-
-  /**
    * Writes file to destination dir. This function is called when an image is
    * dragged from a web page. In this case there is no FileSystem Entry to copy
    * or move, just the JS File object with attached Blob. This operation does
diff --git a/ui/file_manager/file_manager/externs/background/file_operation_manager.js b/ui/file_manager/file_manager/externs/background/file_operation_manager.js
index a0754288..fe41c9c 100644
--- a/ui/file_manager/file_manager/externs/background/file_operation_manager.js
+++ b/ui/file_manager/file_manager/externs/background/file_operation_manager.js
@@ -11,12 +11,6 @@
  */
 export class FileOperationManager {
   /**
-   * Store a reference to our owning File Manager.
-   * @param {Object} fileManager reference to the 'foreground' app.
-   */
-  setFileManager(fileManager) {}
-
-  /**
    * Filters the entry in the same directory
    *
    * @param {Array<Entry>} sourceEntries Entries of the source files.
@@ -39,21 +33,6 @@
   willUseTrash(volumeManager, entries) {}
 
   /**
-   * Notifies File Manager that an extraction operation has finished.
-   *
-   * @param {number} taskId The unique task id for the IO operation.
-   */
-  notifyExtractDone(taskId) {}
-
-  /**
-   * Called when an IOTask finished with a NEED_PASSWORD status.
-   * Delegate it to the task controller to deal with it.
-   *
-   * @param {number} taskId The unique task id for the IO operation.
-   */
-  handleMissingPassword(taskId) {}
-
-  /**
    * Writes file to destination dir.
    *
    * @param {!File} file The file entry to be written.
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 0c0b9e0..9f73c68 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -1251,6 +1251,7 @@
     "//ui/file_manager/file_manager/common/js:dialog_type",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
+    "//ui/file_manager/file_manager/externs:files_app_entry_interfaces",
     "//ui/file_manager/file_manager/externs:volume_manager",
     "//ui/file_manager/file_manager/externs/background:crostini",
     "//ui/file_manager/file_manager/externs/background:progress_center",
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 60e313b..6b03786 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -949,7 +949,6 @@
     }
     this.fileOperationManager_ =
         this.fileBrowserBackground_.fileOperationManager;
-    this.fileOperationManager_.setFileManager(this);
     this.crostini_ = this.fileBrowserBackground_.crostini;
 
     metrics.recordInterval('Load.InitBackgroundPage');
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 7ea76c84..c4efe676 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -2274,20 +2274,8 @@
       if (fileManager.directoryModel.isReadOnly()) {
         dirEntry = fileManager.directoryModel.getMyFiles();
       }
-      this.startExtractTask(fileManager, selectionEntries, dirEntry);
-    }
-  }
-
-  async startExtractTask(fileManager, selectionEntries, dirEntry) {
-    let taskId;
-    try {
-      taskId = await startIOTask(
-          chrome.fileManagerPrivate.IOTaskType.EXTRACT, selectionEntries,
-          {destinationFolder: /** @type {!DirectoryEntry} */ (dirEntry)});
-      fileManager.taskController.storeExtractTaskDetails(
-          taskId, selectionEntries, {destinationFolder: dirEntry});
-    } catch (e) {
-      console.warn('Error getting extract taskID', e);
+      fileManager.taskController.startExtractIOTask(
+          selectionEntries, /** @type {!DirectoryEntry} */ (dirEntry));
     }
   }
 
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller.js b/ui/file_manager/file_manager/foreground/js/task_controller.js
index 7ef1229..e6ce234 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {assert, assertInstanceof, assertNotReached} from 'chrome://resources/js/assert.js';
-import {Command} from './ui/command.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {startIOTask} from '../../common/js/api.js';
@@ -13,6 +12,7 @@
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {Crostini} from '../../externs/background/crostini.js';
 import {ProgressCenter} from '../../externs/background/progress_center.js';
+import {FilesAppDirEntry, FilesAppEntry} from '../../externs/files_app_entry_interfaces.js';
 import {VolumeManager} from '../../externs/volume_manager.js';
 
 import {DirectoryModel} from './directory_model.js';
@@ -23,8 +23,18 @@
 import {MetadataUpdateController} from './metadata_update_controller.js';
 import {NamingController} from './naming_controller.js';
 import {TaskHistory} from './task_history.js';
+import {Command} from './ui/command.js';
 import {FileManagerUI} from './ui/file_manager_ui.js';
 
+/**
+ * Type of the object stashed in the Map extractTasks_.
+ * @typedef {{
+ *   entries: !Array<Entry>,
+ *   params: !chrome.fileManagerPrivate.IOTaskParams,
+ * }}
+ */
+let ExtractingTasks;
+
 export class TaskController {
   /**
    * @param {DialogType} dialogType
@@ -154,10 +164,13 @@
 
     /**
      * Map used to track extract IOTasks in progress.
-     * @private @const {Map}
+     * @private @const {!Map<number, ExtractingTasks>}
      */
     this.extractTasks_ = new Map();
 
+    chrome.fileManagerPrivate.onIOTaskProgressStatus.addListener(
+        this.onIOTaskProgressStatus_.bind(this));
+
     /**
      * Selected entries from the last time onSelectionChanged_ was called.
      * @private {!Array<!Entry>}
@@ -548,32 +561,71 @@
       tasks.executeDefault();
     });
   }
-
-  /**
-   * Stores the task ID and parameters for an extract archive task.
-   */
-  storeExtractTaskDetails(taskId, selectionEntries, parameters) {
-    this.extractTasks_.set(
-        taskId, {'entries': selectionEntries, 'params': parameters});
-  }
-
   /**
    * Removes information about an extract archive task.
+   * @param {number} taskId
+   * @private
    */
-  deleteExtractTaskDetails(taskId) {
+  deleteExtractTaskDetails_(taskId) {
     this.extractTasks_.delete(taskId);
   }
 
   /**
-   * Starts extraction for a single entry and stores the task details.
+   * @param {!chrome.fileManagerPrivate.ProgressStatus} event
    * @private
    */
-  async startExtractTask_(entry, params) {
-    let taskId;
+  onIOTaskProgressStatus_(event) {
+    const taskId = event.taskId;
+    if (!taskId) {
+      console.warn('IOTask ProgressStatus without taskId');
+      return;
+    }
+
+    // TaskController only manages IOTasks related to zip extract that were
+    // started in this window.
+    if (!(this.extractTasks_.has(taskId) &&
+          event.type === chrome.fileManagerPrivate.IOTaskType.EXTRACT)) {
+      return;
+    }
+
+    switch (event.state) {
+      case chrome.fileManagerPrivate.IOTaskState.SUCCESS:
+      case chrome.fileManagerPrivate.IOTaskState.CANCELLED:
+      case chrome.fileManagerPrivate.IOTaskState.ERROR:
+        this.deleteExtractTaskDetails_(taskId);
+        break;
+      case chrome.fileManagerPrivate.IOTaskState.NEED_PASSWORD:
+        this.handleMissingPassword_(taskId);
+        break;
+    }
+  }
+
+  /**
+   * Starts the Zip extract Here IO Task.
+   * @param {!Array<!Entry|FilesAppEntry>} entries
+   * @param {!DirectoryEntry|!FilesAppDirEntry} destination
+   * @return {!Promise<number>} resolved with taskId.
+   */
+  async startExtractIOTask(entries, destination) {
+    const params = {
+      destinationFolder: /** @type {!DirectoryEntry} */ (destination),
+    };
+    return this.startExtractTask_(entries, params);
+  }
+
+  /**
+   * Starts extraction for a single entry and stores the task details.
+   * @param {!Array<!Entry|FilesAppEntry>} entries
+   * @param {!chrome.fileManagerPrivate.IOTaskParams} params
+   * @return {!Promise<number>} resolved with taskId.
+   * @private
+   */
+  async startExtractTask_(entries, params) {
     try {
-      taskId = await startIOTask(
-          chrome.fileManagerPrivate.IOTaskType.EXTRACT, [entry], params);
-      this.storeExtractTaskDetails(taskId, [entry], params);
+      const taskId = await startIOTask(
+          chrome.fileManagerPrivate.IOTaskType.EXTRACT, entries, params);
+      this.extractTasks_.set(taskId, {entries, params});
+      return taskId;
     } catch (e) {
       console.warn('Error getting extract taskID', e);
     }
@@ -603,8 +655,10 @@
    * If an extract operation has finished due to missing password,
    * see if we have the operation stored and if so, pop up a password
    * dialog and try to restart another IO operation for it.
+   * @param {number} taskId
+   * @private
    */
-  handleMissingPassword(taskId) {
+  handleMissingPassword_(taskId) {
     const existingOperation = this.extractTasks_.get(taskId);
     if (existingOperation) {
       // If we have multiple entries (from a multi-select extract) then
@@ -618,11 +672,11 @@
             existingOperation['entries'][0], params);
       } else {
         for (const entry of selectionEntries) {
-          this.startExtractTask_(entry, params);
+          this.startExtractTask_([entry], params);
         }
       }
     }
     // Remove the failed operation reference since it's finished.
-    this.deleteExtractTaskDetails(taskId);
+    this.deleteExtractTaskDetails_(taskId);
   }
 }
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
index ade1f39..fc1b9e8 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
@@ -140,6 +140,9 @@
       NO_SERVICE: 'NO_SERVICE',
     },
     getFileTaskCalledCount_: 0,
+    onIOTaskProgressStatus: {
+      addListener: function(callback) {},
+    },
     getFileTasks: function(entries, callback) {
       mockChrome.fileManagerPrivate.getFileTaskCalledCount_++;
       const fileTasks = ([
diff --git a/ui/gfx/geometry/transform.h b/ui/gfx/geometry/transform.h
index 038f23d..2d2ec9c 100644
--- a/ui/gfx/geometry/transform.h
+++ b/ui/gfx/geometry/transform.h
@@ -89,16 +89,15 @@
                      r0c3, r1c3, r2c3, r3c3);  // col 3
   }
 
-  // TODO(crbug.com/1359528): This is temporary for unit tests to create an
-  // arbitrary affine transform with values without specific meanings, before
-  // the order of parameters of Affine() is fixed.
-  static Transform AffineForTesting(double v0,
-                                    double v1,
-                                    double v2,
-                                    double v3,
-                                    double v4,
-                                    double v5) {
-    return Affine(v0, v1, v2, v3, v4, v5);
+  // Creates a transform from explicit 2d elements. All other matrix elements
+  // remain the same as the corresponding elements of an identity matrix.
+  static Transform Affine(double a,    // a.k.a. r0c0 or scale_x
+                          double b,    // a.k.a. r1c0 or tan(skew_y)
+                          double c,    // a.k.a. r0c1 or tan(skew_x) 
+                          double d,    // a.k.a  r1c1 or scale_y
+                          double e,    // a.k.a  r0c3 or translation_x
+                          double f) {  // a.k.a  r1c3 or translaiton_y
+    return ColMajor(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, e, f, 0, 1);
   }
 
   // Constructs a transform corresponding to the given quaternion.
@@ -114,9 +113,9 @@
     return Transform(sx, sy, 0, 0);
   }
   // Accurately rotate by 90, 180 or 270 degrees about the z axis.
-  static Transform Make90degRotation() { return Affine(0, -1, 1, 0, 0, 0); }
+  static Transform Make90degRotation() { return Affine(0, 1, -1, 0, 0, 0); }
   static Transform Make180degRotation() { return MakeScale(-1); }
-  static Transform Make270degRotation() { return Affine(0, 1, -1, 0, 0, 0); }
+  static Transform Make270degRotation() { return Affine(0, -1, 1, 0, 0, 0); }
 
   // Returns a const reference to an identity transform. If you just need an
   // identity transform as a value, the default constructor is better.
@@ -447,24 +446,6 @@
             double r3c3);
   Transform(float scale_x, float scale_y, float trans_x, float trans_y);
 
-  // TODO(crbug.com/1359528): This is temporarily private before the order of
-  // the parameters is fixed. The current order is weird, not conforming to the
-  // normal order of (a, b, c, d, e, f) which is
-  // (r0c0, r1c0, r0c1, r1c1, r0c3, r1c3).
-  // Creates a transform from explicit 2d elements. All other matrix elements
-  // remain the same as the corresponding elements of an identity matrix.
-  static Transform Affine(double r0c0,
-                          double r0c1,
-                          double r1c0,
-                          double r1c1,
-                          double x_translation,
-                          double y_translation) {
-    return ColMajor(r0c0, r1c0, 0, 0,                     // col 0
-                    r0c1, r1c1, 0, 0,                     // col 1
-                    0, 0, 1, 0,                           // col 2
-                    x_translation, y_translation, 0, 1);  // col 3
-  }
-
   Point3F MapPointInternal(const Matrix44& xform, const Point3F& point) const;
 
   Matrix44 GetFullMatrix() const;
diff --git a/ui/gfx/geometry/transform_unittest.cc b/ui/gfx/geometry/transform_unittest.cc
index 0059c2d..6406e60 100644
--- a/ui/gfx/geometry/transform_unittest.cc
+++ b/ui/gfx/geometry/transform_unittest.cc
@@ -204,7 +204,7 @@
 
 // This test is to make it easier to understand the order of operations.
 TEST(XFormTest, PrePostOperations) {
-  auto m1 = Transform::AffineForTesting(1, 2, 3, 4, 5, 6);
+  auto m1 = Transform::Affine(1, 2, 3, 4, 5, 6);
   auto m2 = m1;
   m1.Translate(10, 20);
   m2.PreConcat(Transform::MakeTranslation(10, 20));
@@ -1415,35 +1415,45 @@
   EXPECT_ROW4_EQ(13.0f, 17.0f, 21.0f, 25.0f, B);
 }
 
-TEST(XFormTest, verifyConstructorFor16Elements) {
+TEST(XFormTest, RowMajor) {
   auto transform =
-      Transform::RowMajor(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
-                          11.0, 12.0, 13.0, 14.0, 15.0, 16.0);
+      Transform::RowMajor(2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
+                          12.0, 13.0, 14.0, 15.0, 16.0, 17.0);
 
-  EXPECT_ROW1_EQ(1.0f, 2.0f, 3.0f, 4.0f, transform);
-  EXPECT_ROW2_EQ(5.0f, 6.0f, 7.0f, 8.0f, transform);
-  EXPECT_ROW3_EQ(9.0f, 10.0f, 11.0f, 12.0f, transform);
-  EXPECT_ROW4_EQ(13.0f, 14.0f, 15.0f, 16.0f, transform);
+  EXPECT_ROW1_EQ(2.0f, 3.0f, 4.0f, 5.0f, transform);
+  EXPECT_ROW2_EQ(6.0f, 7.0f, 8.0f, 9.0f, transform);
+  EXPECT_ROW3_EQ(10.0f, 11.0f, 12.0f, 13.0f, transform);
+  EXPECT_ROW4_EQ(14.0f, 15.0f, 16.0f, 17.0f, transform);
 }
 
-TEST(XFormTest, verifyConstructorFor2dElements) {
-  Transform transform =
-      Transform::AffineForTesting(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
+TEST(XFormTest, ColMajor) {
+  auto transform =
+      Transform::ColMajor(2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
+                          12.0, 13.0, 14.0, 15.0, 16.0, 17.0);
 
-  EXPECT_ROW1_EQ(1.0f, 2.0f, 0.0f, 5.0f, transform);
-  EXPECT_ROW2_EQ(3.0f, 4.0f, 0.0f, 6.0f, transform);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, transform);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, transform);
+  EXPECT_ROW1_EQ(2.0, 6.0, 10.0, 14.0, transform);
+  EXPECT_ROW2_EQ(3.0, 7.0, 11.0, 15.0, transform);
+  EXPECT_ROW3_EQ(4.0, 8.0, 12.0, 16.0, transform);
+  EXPECT_ROW4_EQ(5.0, 9.0, 13.0, 17.0, transform);
+}
+
+TEST(XFormTest, Affine) {
+  auto transform = Transform::Affine(2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
+
+  EXPECT_ROW1_EQ(2.0, 4.0, 0.0, 6.0, transform);
+  EXPECT_ROW2_EQ(3.0, 5.0, 0.0, 7.0, transform);
+  EXPECT_ROW3_EQ(0.0, 0.0, 1.0, 0.0, transform);
+  EXPECT_ROW4_EQ(0.0, 0.0, 0.0, 1.0, transform);
 }
 
 TEST(XFormTest, ColMajorF) {
-  float data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+  float data[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
   auto transform = Transform::ColMajorF(data);
 
-  EXPECT_ROW1_EQ(1.0f, 5.0f, 9.0f, 13.0f, transform);
-  EXPECT_ROW2_EQ(2.0f, 6.0f, 10.0f, 14.0f, transform);
-  EXPECT_ROW3_EQ(3.0f, 7.0f, 11.0f, 15.0f, transform);
-  EXPECT_ROW4_EQ(4.0f, 8.0f, 12.0f, 16.0f, transform);
+  EXPECT_ROW1_EQ(2.0, 6.0, 10.0, 14.0, transform);
+  EXPECT_ROW2_EQ(3.0, 7.0, 11.0, 15.0, transform);
+  EXPECT_ROW3_EQ(4.0, 8.0, 12.0, 16.0, transform);
+  EXPECT_ROW4_EQ(5.0, 9.0, 13.0, 17.0, transform);
 
   float data1[16];
   transform.GetColMajorF(data1);