diff --git a/DEPS b/DEPS
index adc518a..cc71360e 100644
--- a/DEPS
+++ b/DEPS
@@ -297,7 +297,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '79633dd54c6fa17b311dbb94fd3390394edb0bac',
+  'skia_revision': '38cd049145b2b22f2e6e89b10c3a80d1c19b92ff',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -305,7 +305,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': '29d18b303189bf31b3af3a85c3df6013114eebc8',
+  'angle_revision': '8ef496b155e98569ea0463d2dc3556f1d990cc4a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -313,7 +313,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '213704d5b7415001c8851d7dc0dbb33c7a89a4d4',
+  'pdfium_revision': 'c49ebf0b57c81f87aa2958f09459d9ebce7f877d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -324,7 +324,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:9.20220817.2.1',
+  'fuchsia_version': 'version:9.20220819.1.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -376,7 +376,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': 'c005e44cd0f38aea3bac8e0c723c6d89d144d7c3',
+  'devtools_frontend_revision': '0c9c9ed6192b7857f2c0606fa2325cdd081e79fc',
   # 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.
@@ -412,7 +412,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': 'caa9baed54424b14a75776b5c8968924658e5412',
+  'dawn_revision': '9de123fc85226af6c7fc8722666bdf92d6687bb4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -773,7 +773,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '1b05569731b9b2d4f945fa3c87f3fa8a15a732e6',
+    'url': Var('chromium_git') + '/website.git' + '@' + '32cb4368116e8eb8d71e22fcf3a50397c5cb144b',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1174,7 +1174,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '354de5ff0d73b10dbd42ecfaf393dbd26ec62bee',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'dc5729d39a84b6630466d6a522ebff95580bbb32',
       'condition': 'checkout_linux',
   },
 
@@ -1465,7 +1465,7 @@
     Var('chromium_git') + '/webm/libwebp.git' + '@' +  '7366f7f394af26de814296152c50e673ed0a832f',
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '65e7c9d5706a77d1949da59bfcb0817c252ef8d6',
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '3e38ce50589d9319badc0501f96d6c5b2b177472',
 
   'src/third_party/lighttpd': {
       'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'),
@@ -1556,7 +1556,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'fac04ceb3e966f613ed17e98178e9d690280bba6',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '7f795b08c7e41abdecab742d1577cf22c40dadd7',
+    Var('chromium_git') + '/openscreen' + '@' + '3042d1b93e5793e2d8db167ae76e7ae6ec786725',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245',
@@ -1693,7 +1693,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@31a5e38f3e48d6706f5ccb2d2b74eeda1de6a21a',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@41bdd557a6ae1523a6c5a4067942ad59fe62210f',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1729,10 +1729,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '44e4c8770158c505b03ee7feafa4859d083b0912',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '32bacfa266bfdb2c1ddc0170d499f4d0f74569b2',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'fef5f14679a5ec7185835be9142220bd8b79db6c',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '7aaeb5a270ba23f5844f7301a50aaff9b6ca6126',
+    Var('webrtc_git') + '/src.git' + '@' + 'df4dc3ca6ba58218ba28c30544a89ff86c798b37',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1772,7 +1772,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': '58nNno6pNLLSJaZknYmuijuo5gy2tfRBKNI1iCldDlcC',
+          'version': 'nOvtL4uNJDJw0xbMHz5X0t6RPhp-WQ7eFnxDpCkrmlEC',
         },
       ],
       'dep_type': 'cipd',
@@ -1805,7 +1805,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1119a956c163dbb8513202bbc6ae3284ea389a7e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bd365a04c54a402c3683a8dd831f443de09aae48',
     'condition': 'checkout_src_internal',
   },
 
@@ -1846,7 +1846,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'OUGohHk5YvTdODZGYUatmAIbkUeI9qX41jPUFsn8kFAC',
+        'version': 'zCrKLiPCz5bh2lVC4vnvgOsqADfZs68OXYymyBpbb-0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1857,7 +1857,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'xbs9VsAB3uK9gNyQtNWmfxYtOUrdR28ynajJYzrV6tAC',
+        'version': 'PnoXr4Z_iPPPP3Y8_CPucOz1BAE_hW6pbAkkXdTxYb0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index 80b530c..83f6d4bd 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -69,10 +69,14 @@
 const base::Feature kEnableVirtioBlkForData{"ArcEnableVirtioBlkForData",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Used for overriding config params for the virtio-blk feature above.
+const base::Feature kVirtioBlkDataConfigOverride{
+    "ArcVirtioBlkDataConfigOverride", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to use the LVM-provided disk as the backend device for
 // Android /data instead of using the concierge-provided disk.
-const base::FeatureParam<bool> kEnableVirtioBlkForDataUseLvm{
-    &kEnableVirtioBlkForData, "use_lvm", false};
+const base::FeatureParam<bool> kVirtioBlkDataConfigUseLvm{
+    &kVirtioBlkDataConfigOverride, "use_lvm", false};
 
 // Controls experimental file picker feature for ARC.
 const base::Feature kFilePickerExperimentFeature{
diff --git a/ash/components/arc/arc_features.h b/ash/components/arc/arc_features.h
index 21e1f57f5..1afa2b4 100644
--- a/ash/components/arc/arc_features.h
+++ b/ash/components/arc/arc_features.h
@@ -24,7 +24,8 @@
 extern const base::Feature kEnableUnmanagedToManagedTransitionFeature;
 extern const base::Feature kEnableUsap;
 extern const base::Feature kEnableVirtioBlkForData;
-extern const base::FeatureParam<bool> kEnableVirtioBlkForDataUseLvm;
+extern const base::Feature kVirtioBlkDataConfigOverride;
+extern const base::FeatureParam<bool> kVirtioBlkDataConfigUseLvm;
 extern const base::Feature kFilePickerExperimentFeature;
 extern const base::Feature kGameModeFeature;
 extern const base::Feature kGmsCoreLowMemoryKillerProtection;
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc
index 1746350..4560533 100644
--- a/ash/components/arc/session/arc_vm_client_adapter.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -978,7 +978,8 @@
       return;
     }
 
-    if (kEnableVirtioBlkForDataUseLvm.Get()) {
+    if (base::FeatureList::IsEnabled(kVirtioBlkDataConfigOverride) &&
+        kVirtioBlkDataConfigUseLvm.Get()) {
       VLOG(1) << "Using virtio-blk with the LVM-provided disk for /data";
 
       // LVM disk name is generated by cryptohome::DmcryptVolumePrefix in
@@ -990,6 +991,7 @@
                  base::FilePath(lvm_disk_path));
       return;
     }
+    // TODO(b/226686468): Use LVM if lvm_stateful_partition USE flag is enabled.
 
     VLOG(1) << "Using virtio-blk with the concierge-provided disk for /data";
 
diff --git a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
index 1b341bd..3ea2f17 100644
--- a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -1569,8 +1569,7 @@
 
 TEST_F(ArcVmClientAdapterTest, VirtioBlkForData_CreateDiskimageResponseEmpty) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(arc::kEnableVirtioBlkForData,
-                                                  {{"use_lvm", "false"}});
+  feature_list.InitAndEnableFeature(arc::kEnableVirtioBlkForData);
 
   // CreateDiskImage() returns an empty response.
   GetTestConciergeClient()->set_create_disk_image_response(absl::nullopt);
@@ -1584,8 +1583,7 @@
 
 TEST_F(ArcVmClientAdapterTest, VirtioBlkForData_CreateDiskImageStatusFailed) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(arc::kEnableVirtioBlkForData,
-                                                  {{"use_lvm", "false"}});
+  feature_list.InitAndEnableFeature(arc::kEnableVirtioBlkForData);
 
   GetTestConciergeClient()->set_create_disk_image_response(
       CreateDiskImageResponse(vm_tools::concierge::DISK_STATUS_FAILED));
@@ -1599,8 +1597,7 @@
 
 TEST_F(ArcVmClientAdapterTest, VirtioBlkForData_CreateDiskImageStatusCreated) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(arc::kEnableVirtioBlkForData,
-                                                  {{"use_lvm", "false"}});
+  feature_list.InitAndEnableFeature(arc::kEnableVirtioBlkForData);
 
   GetTestConciergeClient()->set_create_disk_image_response(
       CreateDiskImageResponse(vm_tools::concierge::DISK_STATUS_CREATED));
@@ -1619,8 +1616,7 @@
 
 TEST_F(ArcVmClientAdapterTest, VirtioBlkForData_CreateDiskImageStatusExists) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(arc::kEnableVirtioBlkForData,
-                                                  {{"use_lvm", "false"}});
+  feature_list.InitAndEnableFeature(arc::kEnableVirtioBlkForData);
 
   GetTestConciergeClient()->set_create_disk_image_response(
       CreateDiskImageResponse(vm_tools::concierge::DISK_STATUS_EXISTS));
@@ -1639,8 +1635,10 @@
 
 TEST_F(ArcVmClientAdapterTest, VirtioBlkForData_UseLvm) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(arc::kEnableVirtioBlkForData,
-                                                  {{"use_lvm", "true"}});
+  feature_list.InitWithFeaturesAndParameters(
+      {{arc::kEnableVirtioBlkForData, {{}}},
+       {arc::kVirtioBlkDataConfigOverride, {{"use_lvm", "true"}}}},
+      {});
 
   StartMiniArcWithParams(true, GetPopulatedStartParams());
   EXPECT_GE(GetTestConciergeClient()->start_arc_vm_call_count(), 1);
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index a7245968..ede7c22 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -298,7 +298,7 @@
 
 // Enables to allow using document scanning feature via DLC in the camera app.
 const base::Feature kCameraAppDocScanDlc{"CameraAppDocScanDlc",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether the camera privacy switch toasts and notification should be
 // displayed.
@@ -429,6 +429,10 @@
 const base::Feature kBruschetta{"Bruschetta",
                                 base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables or disables migration for third party VMs installed during alpha.
+const base::Feature kBruschettaAlphaMigrate{"BruschettaAlphaMigrate",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables or disables always using device-activity-status data to filter
 // eligible host phones.
 const base::Feature kCryptAuthV2AlwaysUseActiveEligibleHosts{
@@ -693,6 +697,11 @@
 const base::Feature kExperimentalRgbKeyboardPatterns{
     "ExperimentalRgbKeyboardPatterns", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the System Web App (SWA) of Face ML.
+// This app needs both CrOS and hardware support (Face Auth Camera and System
+// Face Auth Service), therefore we only enable it on these eligible devices.
+const base::Feature kFaceMlApp{"FaceMlApp", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables policy that controls feature to allow Family Link accounts on school
 // owned devices.
 const base::Feature kFamilyLinkOnSchoolDevice{"FamilyLinkOnSchoolDevice",
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index aa2cc02..48b030ce 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -181,6 +181,8 @@
 extern const base::Feature kGuestOSGenericInstaller;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kBruschetta;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kBruschettaAlphaMigrate;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptAuthV2AlwaysUseActiveEligibleHosts;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptAuthV2DeviceActivityStatus;
@@ -296,6 +298,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kExoOrdinalMotion;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kExperimentalRgbKeyboardPatterns;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFaceMlApp;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kFamilyLinkOnSchoolDevice;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFastPair;
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index 88449c91..bc95ea7 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -877,6 +877,15 @@
 const char kWaitForInitialPolicyFetchForTest[] =
     "wait-for-initial-policy-fetch-for-test";
 
+// If provided, any webui will be loaded from <flag value>/<handler_name>, where
+// handler_name is the name passed to MaybeConfigureTestableDataSource, if the
+// file exists.
+// For example, if the flag is /tmp/resource_overrides, attempting to load
+// js/app_main.js from the data source named "help_app/untrusted" will first
+// attempt to load from /tmp/resource_overrides/help_app/untrusted/js/main.js.
+const char kWebUiDataSourcePathForTesting[] =
+    "web-ui-data-source-path-for-testing";
+
 // Used to determine if and how on-device handwriting recognition is supported
 // (e.g. via rootfs or downloadable content).
 const char kOndeviceHandwritingSwitch[] = "ondevice_handwriting";
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index 5a21490..454d6ec2 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -283,6 +283,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kUpdateRequiredAueForTest[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kWaitForInitialPolicyFetchForTest[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kWebUiDataSourcePathForTesting[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kOndeviceHandwritingSwitch[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kGetAccessTokenForTest[];
 
diff --git a/ash/constants/notifier_catalogs.h b/ash/constants/notifier_catalogs.h
index 1ac119f6a..fe4a810 100644
--- a/ash/constants/notifier_catalogs.h
+++ b/ash/constants/notifier_catalogs.h
@@ -169,7 +169,8 @@
 };
 
 // A living catalog that registers system nudges.
-// Current values should not be renumbered or removed.
+// Current values should not be renumbered or removed. Please keep in sync with
+// "NudgeCatalogName" in tools/metrics/histograms/enums.xml.
 // To deprecate comment out the entry.
 enum class NudgeCatalogName {
   kTestCatalogName = 0,
diff --git a/ash/system/message_center/ash_notification_view.cc b/ash/system/message_center/ash_notification_view.cc
index ef35f19..05e9b919 100644
--- a/ash/system/message_center/ash_notification_view.cc
+++ b/ash/system/message_center/ash_notification_view.cc
@@ -48,6 +48,7 @@
 #include "ui/gfx/geometry/transform.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/text_constants.h"
 #include "ui/gfx/text_elider.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notification_view_controller.h"
@@ -73,6 +74,7 @@
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/layout/layout_types.h"
+#include "ui/views/layout/table_layout.h"
 #include "ui/views/metadata/view_factory_internal.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/view.h"
@@ -95,6 +97,7 @@
 constexpr auto kContentRowPadding = gfx::Insets::TLBR(16, 0, 0, 0);
 
 constexpr int kLeftContentVerticalSpacing = 4;
+constexpr int kTitleRowMinimumWidth = 186;
 constexpr int kTitleRowSpacing = 6;
 
 constexpr auto kHeaderRowExpandedPadding = gfx::Insets::TLBR(4, 0, 8, 0);
@@ -277,11 +280,24 @@
           views::style::CONTEXT_DIALOG_BODY_TEXT))),
       timestamp_in_collapsed_view_(
           AddChildView(std::make_unique<views::Label>())) {
-  SetLayoutManager(std::make_unique<views::FlexLayout>())
-      ->SetDefault(views::kMarginsKey,
-                   gfx::Insets::TLBR(0, 0, 0, kTitleRowSpacing));
+  SetLayoutManager(std::make_unique<views::TableLayout>())
+      ->AddColumn(views::LayoutAlignment::kStart,
+                  views::LayoutAlignment::kCenter, 1.0,
+                  views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
+      .AddPaddingColumn(views::TableLayout::kFixedSize, kTitleRowSpacing)
+      .AddColumn(views::LayoutAlignment::kStart,
+                 views::LayoutAlignment::kCenter,
+                 views::TableLayout::kFixedSize,
+                 views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
+      .AddPaddingColumn(views::TableLayout::kFixedSize, kTitleRowSpacing)
+      .AddColumn(views::LayoutAlignment::kStart,
+                 views::LayoutAlignment::kCenter, 100.0,
+                 views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
+      .AddRows(1, views::TableLayout::kFixedSize);
+
   timestamp_in_collapsed_view_->SetProperty(views::kMarginsKey,
                                             kTimeStampInCollapsedStatePadding);
+  timestamp_in_collapsed_view_->SetElideBehavior(gfx::ElideBehavior::NO_ELIDE);
   title_view_->SetProperty(
       views::kFlexBehaviorKey,
       views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
@@ -350,6 +366,17 @@
   }
 }
 
+gfx::Size AshNotificationView::NotificationTitleRow::CalculatePreferredSize()
+    const {
+  // TODO(crbug.com/1349528): The size constraint is not passed down from the
+  // views tree in the first round of layout, so setting a fixed width to bound
+  // the view. The layout manager can size the view beyond this width if there
+  // is available space. This works similar to applying a max width on the
+  // internal labels.
+  return gfx::Size(kTitleRowMinimumWidth,
+                   GetHeightForWidth(kTitleRowMinimumWidth));
+}
+
 void AshNotificationView::NotificationTitleRow::OnThemeChanged() {
   views::View::OnThemeChanged();
 
@@ -915,7 +942,6 @@
     title_row_->UpdateVisibility(IsExpandable() && !expanded);
     title_row_->title_view()->SetMaxLines(
         expanded ? kTitleLabelExpandedMaxLines : kTitleLabelCollapsedMaxLines);
-    title_row_->title_view()->SetMaximumWidth(GetExpandedTitleLabelWidth());
   }
 
   if (message_label()) {
@@ -1431,17 +1457,6 @@
           top_radius_, bottom_radius_, background_color_)));
 }
 
-int AshNotificationView::GetExpandedTitleLabelWidth() {
-  int notification_width = shown_in_popup_ ? message_center::kNotificationWidth
-                                           : kNotificationInMessageCenterWidth;
-
-  return notification_width - kNotificationViewPadding.width() -
-         kAppIconViewSize - kMainRightViewChildPadding.width() -
-         kAppIconViewSize - right_content()->width() -
-         kRightContentExpandedPadding.width() -
-         kMessageLabelInExpandedStatePadding.width();
-}
-
 int AshNotificationView::GetExpandedMessageLabelWidth() {
   int notification_width = shown_in_popup_ ? message_center::kNotificationWidth
                                            : kNotificationInMessageCenterWidth;
diff --git a/ash/system/message_center/ash_notification_view.h b/ash/system/message_center/ash_notification_view.h
index 7bd977a..947edb2 100644
--- a/ash/system/message_center/ash_notification_view.h
+++ b/ash/system/message_center/ash_notification_view.h
@@ -170,6 +170,7 @@
     void PerformExpandCollapseAnimation();
 
     // views::View:
+    gfx::Size CalculatePreferredSize() const override;
     void OnThemeChanged() override;
 
     views::Label* title_view() { return title_view_; }
@@ -221,9 +222,6 @@
   // Update the background color with rounded corner.
   void UpdateBackground(int top_radius, int bottom_radius);
 
-  // Get the available space for the notification's title label.
-  int GetExpandedTitleLabelWidth();
-
   // Get the available space for `message_label_in_expanded_state_` width.
   int GetExpandedMessageLabelWidth();
 
diff --git a/ash/system/time/calendar_unittest_utils.h b/ash/system/time/calendar_unittest_utils.h
index be0e0f3c..7fc901d 100644
--- a/ash/system/time/calendar_unittest_utils.h
+++ b/ash/system/time/calendar_unittest_utils.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_TIME_CALENDAR_UNITTEST_UTILS_H_
 #define ASH_SYSTEM_TIME_CALENDAR_UNITTEST_UTILS_H_
 
+#include <set>
 #include <string>
 
 #include "base/time/time.h"
@@ -170,6 +171,44 @@
                                "Pacific/Tongatapu",
                                "Pacific/Apia",
                                "Pacific/Kiritimati"};
+
+// These are from "third_party/fontconfig/include/fc-lang/fclang.h" data.
+// Locales "und-zmth" and "und-zsye" are omitted since they cannot be set.
+const char* kLocales[] = {
+    "aa",     "ab",     "af",     "ak",    "am",    "an",    "ar",    "as",
+    "ast",    "av",     "ay",     "az-az", "az-ir", "ba",    "be",    "ber-dz",
+    "ber-ma", "bg",     "bh",     "bho",   "bi",    "bin",   "bm",    "bn",
+    "bo",     "br",     "brx",    "bs",    "bua",   "byn",   "ca",    "ce",
+    "ch",     "chm",    "chr",    "co",    "crh",   "cs",    "csb",   "cu",
+    "cv",     "cy",     "da",     "de",    "doi",   "dv",    "dz",    "ee",
+    "el",     "en",     "eo",     "es",    "et",    "eu",    "fa",    "fat",
+    "ff",     "fi",     "fil",    "fj",    "fo",    "fr",    "fur",   "fy",
+    "ga",     "gd",     "gez",    "gl",    "gn",    "gu",    "gv",    "ha",
+    "haw",    "he",     "hi",     "hne",   "ho",    "hr",    "hsb",   "ht",
+    "hu",     "hy",     "hz",     "ia",    "id",    "ie",    "ig",    "ii",
+    "ik",     "io",     "is",     "it",    "iu",    "ja",    "jv",    "ka",
+    "kaa",    "kab",    "ki",     "kj",    "kk",    "kl",    "km",    "kn",
+    "ko",     "kok",    "kr",     "ks",    "ku-am", "ku-iq", "ku-ir", "ku-tr",
+    "kum",    "kv",     "kw",     "kwm",   "ky",    "la",    "lah",   "lb",
+    "lez",    "lg",     "li",     "ln",    "lo",    "lt",    "lv",    "mai",
+    "mg",     "mh",     "mi",     "mk",    "ml",    "mn-cn", "mn-mn", "mni",
+    "mo",     "mr",     "ms",     "mt",    "my",    "na",    "nb",    "nds",
+    "ne",     "ng",     "nl",     "nn",    "no",    "nqo",   "nr",    "nso",
+    "nv",     "ny",     "oc",     "om",    "or",    "os",    "ota",   "pa",
+    "pa-pk",  "pap-an", "pap-aw", "pl",    "ps-af", "ps-pk", "pt",    "qu",
+    "quz",    "rm",     "rn",     "ro",    "ru",    "rw",    "sa",    "sah",
+    "sat",    "sc",     "sco",    "sd",    "se",    "sel",   "sg",    "sh",
+    "shs",    "si",     "sid",    "sk",    "sl",    "sm",    "sma",   "smj",
+    "smn",    "sms",    "sn",     "so",    "sq",    "sr",    "ss",    "st",
+    "su",     "sv",     "sw",     "syr",   "ta",    "te",    "tg",    "th",
+    "ti-er",  "ti-et",  "tig",    "tk",    "tl",    "tn",    "to",    "tr",
+    "ts",     "tt",     "tw",     "ty",    "tyv",   "ug",    "uk",    "ur",
+    "uz",     "ve",     "vi",     "vo",    "vot",   "wa",    "wal",   "wen",
+    "wo",     "xh",     "yap",    "yi",    "yo",    "za",    "zh-cn", "zh-hk",
+    "zh-mo",  "zh-sg",  "zh-tw",  "zu"};
+
+std::set<std::string> kLocalesWithUniqueNumerals{"bn", "fa", "mr", "pa-pk"};
+
 }  // namespace
 
 namespace calendar_test_utils {
diff --git a/ash/system/time/calendar_utils.cc b/ash/system/time/calendar_utils.cc
index b4a6a51..2891b9e 100644
--- a/ash/system/time/calendar_utils.cc
+++ b/ash/system/time/calendar_utils.cc
@@ -139,6 +139,22 @@
       DateHelper::GetInstance()->month_name_year_formatter(), date);
 }
 
+std::u16string GetTwelveHourClockHours(const base::Time date) {
+  return calendar_utils::FormatDate(
+      DateHelper::GetInstance()->twelve_hour_clock_hours_formatter(), date);
+}
+
+std::u16string GetTwentyFourHourClockHours(const base::Time date) {
+  return calendar_utils::FormatDate(
+      DateHelper::GetInstance()->twenty_four_hour_clock_hours_formatter(),
+      date);
+}
+
+std::u16string GetMinutes(const base::Time date) {
+  return calendar_utils::FormatDate(
+      DateHelper::GetInstance()->minutes_formatter(), date);
+}
+
 std::u16string FormatTwelveHourClockTimeInterval(const base::Time& start_time,
                                                  const base::Time& end_time) {
   return calendar_utils::FormatInterval(
diff --git a/ash/system/time/calendar_utils.h b/ash/system/time/calendar_utils.h
index 0111203..2975836 100644
--- a/ash/system/time/calendar_utils.h
+++ b/ash/system/time/calendar_utils.h
@@ -122,11 +122,11 @@
 // (e.g. March 10)
 ASH_EXPORT std::u16string GetMonthNameAndDayOfMonth(const base::Time date);
 
-// Gets the `date`'s hour in twelve hour clock format.
-// (e.g. 2:31 AM)
+// Gets the `date`'s time in twelve hour clock format.
+// (e.g. 10:31 PM)
 ASH_EXPORT std::u16string GetTwelveHourClockTime(const base::Time date);
 
-// Gets the `date`'s hour in twenty four hour clock format.
+// Gets the `date`'s time in twenty four hour clock format.
 // (e.g. 22:31)
 ASH_EXPORT std::u16string GetTwentyFourHourClockTime(const base::Time date);
 
@@ -146,6 +146,20 @@
 // (e.g. March 2022)
 ASH_EXPORT std::u16string GetMonthNameAndYear(const base::Time date);
 
+// Gets the `date`'s hour in twelve hour clock format.
+// (e.g. 9, when given 21:05)
+// Some locales may add zero-padding.
+ASH_EXPORT std::u16string GetTwelveHourClockHours(const base::Time date);
+
+// Gets the `date`'s hour in twenty four hour clock format.
+// (e.g. 21, when given 21:05)
+// Some locales may add zero-padding.
+ASH_EXPORT std::u16string GetTwentyFourHourClockHours(const base::Time date);
+
+// Gets the `date`'s minutes with zero-padding.
+// (e.g. 05, when given 22:05)
+ASH_EXPORT std::u16string GetMinutes(const base::Time date);
+
 // Gets the formatted interval between `start_time` and `end_time` in twelve
 // hour clock format.
 // (e.g. 8:30 – 9:30 PM or 11:30 AM – 2:30 PM)
diff --git a/ash/system/time/calendar_utils_unittest.cc b/ash/system/time/calendar_utils_unittest.cc
index 42caff3..478259fef 100644
--- a/ash/system/time/calendar_utils_unittest.cc
+++ b/ash/system/time/calendar_utils_unittest.cc
@@ -5,11 +5,23 @@
 #include "ash/system/time/calendar_utils.h"
 
 #include "ash/components/settings/timezone_settings.h"
+#include "ash/system/time/calendar_unittest_utils.h"
+#include "ash/system/time/date_helper.h"
 #include "ash/test/ash_test_base.h"
+#include "base/i18n/rtl.h"
 #include "base/time/time.h"
 
 namespace ash {
 
+namespace {
+
+void SetDefaultLocale(const std::string& lang) {
+  base::i18n::SetICUDefaultLocale(lang);
+  ash::DateHelper::GetInstance()->ResetForTesting();
+}
+
+}  // namespace
+
 using CalendarUtilsUnittest = AshTestBase;
 
 // Tests the time difference calculation with different timezones and
@@ -40,7 +52,7 @@
 }
 
 TEST_F(CalendarUtilsUnittest, DateFormatter) {
-  // Create a date: Aug,1st 2021.
+  // Create a date: Aug 1, 2021.
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("1 Aug 2021 10:00 GMT", &date));
   ash::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(u"GMT");
@@ -57,9 +69,6 @@
   // Test DateFormatter to return month name and day of month.
   EXPECT_EQ(u"August 1", calendar_utils::GetMonthNameAndDayOfMonth(date));
 
-  // Test DateFormatter to return hour in twelve hour clock format.
-  EXPECT_EQ(u"10:00 AM", calendar_utils::GetTwelveHourClockTime(date));
-
   // Test DateFormatter to return the time zone.
   EXPECT_EQ(u"Greenwich Mean Time", calendar_utils::GetTimeZone(date));
 
@@ -70,6 +79,131 @@
   EXPECT_EQ(u"August 2021", calendar_utils::GetMonthNameAndYear(date));
 }
 
+TEST_F(CalendarUtilsUnittest, DateFormatterClockTimes) {
+  ash::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(u"GMT");
+
+  // Using "en" locale as other languages format their hours differently.
+  SetDefaultLocale("en");
+
+  // Create AM time: 9:05 GMT.
+  base::Time am_time;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 9:05 GMT", &am_time));
+
+  // Create PM time: 23:30 GMT.
+  base::Time pm_time;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 23:30 GMT", &pm_time));
+
+  // Create midnight: 00:00 GMT.
+  base::Time midnight;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 00:00 GMT", &midnight));
+
+  // Return time in twelve hour clock format. (no '0' padding)
+  EXPECT_EQ(u"9:05 AM", calendar_utils::GetTwelveHourClockTime(am_time));
+  EXPECT_EQ(u"11:30 PM", calendar_utils::GetTwelveHourClockTime(pm_time));
+  EXPECT_EQ(u"12:00 AM", calendar_utils::GetTwelveHourClockTime(midnight));
+
+  // Return time in twenty four hour clock format. (has '0' padding)
+  EXPECT_EQ(u"09:05", calendar_utils::GetTwentyFourHourClockTime(am_time));
+  EXPECT_EQ(u"23:30", calendar_utils::GetTwentyFourHourClockTime(pm_time));
+  EXPECT_EQ(u"00:00", calendar_utils::GetTwentyFourHourClockTime(midnight));
+
+  // Return single hours in twelve hour format. (no '0' padding)
+  EXPECT_EQ(u"9", calendar_utils::GetTwelveHourClockHours(am_time));
+  EXPECT_EQ(u"11", calendar_utils::GetTwelveHourClockHours(pm_time));
+  EXPECT_EQ(u"12", calendar_utils::GetTwelveHourClockHours(midnight));
+
+  // Return single hours in twenty four hour format. (has '0' padding)
+  EXPECT_EQ(u"09", calendar_utils::GetTwentyFourHourClockHours(am_time));
+  EXPECT_EQ(u"23", calendar_utils::GetTwentyFourHourClockHours(pm_time));
+  EXPECT_EQ(u"00", calendar_utils::GetTwentyFourHourClockHours(midnight));
+
+  // Return minutes. (has '0' padding)
+  EXPECT_EQ(u"05", calendar_utils::GetMinutes(am_time));
+  EXPECT_EQ(u"30", calendar_utils::GetMinutes(pm_time));
+  EXPECT_EQ(u"00", calendar_utils::GetMinutes(midnight));
+}
+
+TEST_F(CalendarUtilsUnittest, HoursAndMinutesInDifferentLocales) {
+  ash::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(u"GMT");
+
+  // Create AM time: 9:05 GMT.
+  base::Time am_time;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 9:05 GMT", &am_time));
+
+  // Create PM time: 23:30 GMT.
+  base::Time pm_time;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 23:30 GMT", &pm_time));
+
+  // Create midnight: 00:00 GMT.
+  base::Time midnight;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 00:00 GMT", &midnight));
+
+  for (auto* locale : kLocales) {
+    // Skip locales that are tested in "LocalesWithUniqueNumerals".
+    if (kLocalesWithUniqueNumerals.count(locale))
+      continue;
+
+    SetDefaultLocale(locale);
+
+    // If the length of the hour string is more than 1 in a single digit hour
+    // (9AM) then it is zero-padded.
+    bool zero_padded_12H =
+        calendar_utils::GetTwelveHourClockHours(am_time).length() > 1;
+    bool zero_padded_24H =
+        calendar_utils::GetTwentyFourHourClockHours(am_time).length() > 1;
+
+    // Return hours in twelve hour format.
+    EXPECT_EQ(zero_padded_12H ? u"09" : u"9",
+              calendar_utils::GetTwelveHourClockHours(am_time));
+    EXPECT_EQ(u"11", calendar_utils::GetTwelveHourClockHours(pm_time));
+    // Locale 'ja'uses  'K' format (0~11) for its 12-hour clock.
+    EXPECT_EQ((strcmp(locale, "ja") == 0) ? u"0" : u"12",
+              calendar_utils::GetTwelveHourClockHours(midnight));
+
+    // Return hours in twenty four hour format.
+    EXPECT_EQ(zero_padded_24H ? u"09" : u"9",
+              calendar_utils::GetTwentyFourHourClockHours(am_time));
+    EXPECT_EQ(u"23", calendar_utils::GetTwentyFourHourClockHours(pm_time));
+    EXPECT_EQ(zero_padded_24H ? u"00" : u"0",
+              calendar_utils::GetTwentyFourHourClockHours(midnight));
+
+    // Return minutes. (always zero-padded)
+    EXPECT_EQ(u"05", calendar_utils::GetMinutes(am_time));
+    EXPECT_EQ(u"30", calendar_utils::GetMinutes(pm_time));
+    EXPECT_EQ(u"00", calendar_utils::GetMinutes(midnight));
+  }
+
+  // Reset locale to English for subsequent tests.
+  SetDefaultLocale("en");
+}
+
+TEST_F(CalendarUtilsUnittest, LocalesWithUniqueNumerals) {
+  ash::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(u"GMT");
+
+  // Create time: 23:03 GMT.
+  base::Time time;
+  ASSERT_TRUE(base::Time::FromString("1 Aug 2021 23:03 GMT", &time));
+
+  for (auto locale : kLocalesWithUniqueNumerals) {
+    SetDefaultLocale(locale);
+
+    if (locale == "bn") {
+      EXPECT_EQ(u"২৩", calendar_utils::GetTwentyFourHourClockHours(time));
+      EXPECT_EQ(u"০৩", calendar_utils::GetMinutes(time));
+    } else if (locale == "fa" || locale == "pa-pk") {
+      EXPECT_EQ(u"Û²Û³", calendar_utils::GetTwentyFourHourClockHours(time));
+      EXPECT_EQ(u"Û°Û³", calendar_utils::GetMinutes(time));
+    } else if (locale == "mr") {
+      EXPECT_EQ(u"२३", calendar_utils::GetTwentyFourHourClockHours(time));
+      EXPECT_EQ(u"०३", calendar_utils::GetMinutes(time));
+    } else
+      EXPECT_TRUE(false) << "Locale '" << locale << "' needs a test case.";
+  }
+
+  // Reset locale to English for subsequent tests.
+  SetDefaultLocale("en");
+}
+
 TEST_F(CalendarUtilsUnittest, IntervalFormatter) {
   base::Time date1;
   base::Time date2;
diff --git a/ash/system/time/date_helper.cc b/ash/system/time/date_helper.cc
index 115f7330..b076f83 100644
--- a/ash/system/time/date_helper.cc
+++ b/ash/system/time/date_helper.cc
@@ -6,6 +6,7 @@
 
 #include "ash/shell.h"
 #include "ash/system/locale/locale_update_controller_impl.h"
+#include "ash/system/model/clock_model.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/time/calendar_utils.h"
 #include "base/i18n/unicodestring.h"
@@ -33,6 +34,33 @@
                             base::Time::kMillisecondsPerSecond);
 }
 
+// Receives an input `unicode_pattern` in the "Hm" format (HH:mm, aK:mm, h:mm a,
+// a hh:mm, etc.) and extracts the hours part of the pattern.
+icu::UnicodeString getHoursPattern(const icu::UnicodeString& unicode_pattern) {
+  std::string pattern;
+  unicode_pattern.toUTF8String(pattern);
+
+  if (pattern.find("hh") != std::string::npos)
+    return icu::UnicodeString("hh");
+  if (pattern.find("h") != std::string::npos)
+    return icu::UnicodeString("h");
+  if (pattern.find("HH") != std::string::npos)
+    return icu::UnicodeString("HH");
+  if (pattern.find("H") != std::string::npos)
+    return icu::UnicodeString("H");
+  if (pattern.find("KK") != std::string::npos)
+    return icu::UnicodeString("KK");
+  if (pattern.find("K") != std::string::npos)
+    return icu::UnicodeString("K");
+  if (pattern.find("kk") != std::string::npos)
+    return icu::UnicodeString("kk");
+  if (pattern.find("k") != std::string::npos)
+    return icu::UnicodeString("k");
+
+  NOTREACHED() << "Hours pattern not found.";
+  return icu::UnicodeString("HH");
+}
+
 }  // namespace
 
 // static
@@ -55,13 +83,22 @@
       generator->getBestPattern(icu::UnicodeString(pattern), status);
   DCHECK(U_SUCCESS(status));
 
-  // Then, format the time using the generated pattern.
+  // Then, create a formatter object using the generated pattern.
   icu::SimpleDateFormat formatter(generated_pattern, status);
   DCHECK(U_SUCCESS(status));
 
   return formatter;
 }
 
+icu::SimpleDateFormat DateHelper::CreateSimpleDateFormatterWithoutBestPattern(
+    const char* pattern) {
+  UErrorCode status = U_ZERO_ERROR;
+  DCHECK(U_SUCCESS(status));
+  icu::SimpleDateFormat formatter(icu::UnicodeString(pattern), status);
+  DCHECK(U_SUCCESS(status));
+  return formatter;
+}
+
 std::unique_ptr<icu::DateIntervalFormat>
 DateHelper::CreateDateIntervalFormatter(const char* pattern) {
   UErrorCode status = U_ZERO_ERROR;
@@ -71,6 +108,24 @@
   return absl::WrapUnique(formatter);
 }
 
+icu::SimpleDateFormat DateHelper::CreateHoursFormatter(const char* pattern) {
+  UErrorCode status = U_ZERO_ERROR;
+  DCHECK(U_SUCCESS(status));
+  std::unique_ptr<icu::DateTimePatternGenerator> generator(
+      icu::DateTimePatternGenerator::createInstance(status));
+  DCHECK(U_SUCCESS(status));
+  icu::UnicodeString generated_pattern =
+      generator->getBestPattern(icu::UnicodeString(pattern), status);
+  DCHECK(U_SUCCESS(status));
+
+  // Extract the hours from the generated pattern.
+  icu::UnicodeString hours_pattern = getHoursPattern(generated_pattern);
+  icu::SimpleDateFormat formatter(hours_pattern, status);
+  DCHECK(U_SUCCESS(status));
+
+  return formatter;
+}
+
 std::u16string DateHelper::GetFormattedTime(const icu::DateFormat* formatter,
                                             const base::Time& time) {
   DCHECK(formatter);
@@ -143,6 +198,9 @@
       day_of_week_formatter_(CreateSimpleDateFormatter("ee")),
       week_title_formatter_(CreateSimpleDateFormatter("EEEEE")),
       year_formatter_(CreateSimpleDateFormatter("YYYY")),
+      twelve_hour_clock_hours_formatter_(CreateHoursFormatter("h:mm a")),
+      twenty_four_hour_clock_hours_formatter_(CreateHoursFormatter("HH:mm")),
+      minutes_formatter_(CreateSimpleDateFormatterWithoutBestPattern("mm")),
       twelve_hour_clock_interval_formatter_(CreateDateIntervalFormatter("hm")),
       twenty_four_hour_clock_interval_formatter_(
           CreateDateIntervalFormatter("Hm")) {
@@ -179,6 +237,9 @@
   day_of_week_formatter_ = CreateSimpleDateFormatter("ee");
   week_title_formatter_ = CreateSimpleDateFormatter("EEEEE");
   year_formatter_ = CreateSimpleDateFormatter("YYYY");
+  twelve_hour_clock_hours_formatter_ = CreateHoursFormatter("h:mm a");
+  twenty_four_hour_clock_hours_formatter_ = CreateHoursFormatter("HH:mm");
+  minutes_formatter_ = CreateSimpleDateFormatterWithoutBestPattern("mm");
   twelve_hour_clock_interval_formatter_ = CreateDateIntervalFormatter("hm");
   twenty_four_hour_clock_interval_formatter_ =
       CreateDateIntervalFormatter("Hm");
@@ -242,6 +303,7 @@
   gregorian_calendar_->setTimeZone(
       system::TimezoneSettings::GetInstance()->GetTimezone());
   Shell::Get()->system_tray_model()->calendar_model()->RedistributeEvents();
+  Shell::Get()->system_tray_model()->clock()->NotifyRefreshClock();
 }
 
 void DateHelper::OnLocaleChanged() {
diff --git a/ash/system/time/date_helper.h b/ash/system/time/date_helper.h
index 01680cf1..f13d52a0 100644
--- a/ash/system/time/date_helper.h
+++ b/ash/system/time/date_helper.h
@@ -35,6 +35,17 @@
   // Creates a formatter object used to format dates from the given `pattern`.
   icu::SimpleDateFormat CreateSimpleDateFormatter(const char* pattern);
 
+  // Creates a formatter object used to format dates without calling the
+  // `getBestPattern` function, which resolves the input pattern to the best
+  // fit, which is not always what we want. e.g. 'mm' returns 'm' even though we
+  // want it zero-padded (03 vs. 3 when given 12:03)
+  icu::SimpleDateFormat CreateSimpleDateFormatterWithoutBestPattern(
+      const char* pattern);
+
+  // Creates a formatter object that extracts the hours field from a given date.
+  // Uses `pattern` to differentiate between 12 and 24 hour clock formats.
+  icu::SimpleDateFormat CreateHoursFormatter(const char* pattern);
+
   // Creates a date interval formatter object that formats a `DateInterval` into
   // text as compactly as possible.
   // Note that even if a pattern does not request a certain date part, it will
@@ -108,6 +119,16 @@
 
   icu::SimpleDateFormat& year_formatter() { return year_formatter_; }
 
+  icu::SimpleDateFormat& twelve_hour_clock_hours_formatter() {
+    return twelve_hour_clock_hours_formatter_;
+  }
+
+  icu::SimpleDateFormat& twenty_four_hour_clock_hours_formatter() {
+    return twenty_four_hour_clock_hours_formatter_;
+  }
+
+  icu::SimpleDateFormat& minutes_formatter() { return minutes_formatter_; }
+
   const icu::DateIntervalFormat* twelve_hour_clock_interval_formatter() {
     return twelve_hour_clock_interval_formatter_.get();
   }
@@ -184,6 +205,15 @@
   // Formatter for getting the year.
   icu::SimpleDateFormat year_formatter_;
 
+  // Formatter for getting the hours in a 12 hour clock format.
+  icu::SimpleDateFormat twelve_hour_clock_hours_formatter_;
+
+  // Formatter for getting the hours in a 24 hour clock format.
+  icu::SimpleDateFormat twenty_four_hour_clock_hours_formatter_;
+
+  // Formatter for getting the minutes.
+  icu::SimpleDateFormat minutes_formatter_;
+
   // Interval formatter for two dates. Formats time in twelve
   // hour clock format (e.g. 8:30 – 9:30 PM or 11:30 AM – 2:30 PM).
   std::unique_ptr<icu::DateIntervalFormat>
diff --git a/ash/system/time/date_helper_unittest.cc b/ash/system/time/date_helper_unittest.cc
index 62eb870..d26556f 100644
--- a/ash/system/time/date_helper_unittest.cc
+++ b/ash/system/time/date_helper_unittest.cc
@@ -4,53 +4,12 @@
 
 #include "ash/system/time/date_helper.h"
 
+#include "ash/system/time/calendar_unittest_utils.h"
 #include "ash/test/ash_test_base.h"
 #include "base/i18n/rtl.h"
 
 namespace ash {
 
-namespace {
-
-// These are from "third_party/fontconfig/include/fc-lang/fclang.h" data.
-const char* kLocales[] = {
-    "aa",       "ab",     "af",     "ak",    "am",    "an",    "ar",
-    "as",       "ast",    "av",     "ay",    "az-az", "az-ir", "ba",
-    "be",       "ber-dz", "ber-ma", "bg",    "bh",    "bho",   "bi",
-    "bin",      "bm",     "bn",     "bo",    "br",    "brx",   "bs",
-    "bua",      "byn",    "ca",     "ce",    "ch",    "chm",   "chr",
-    "co",       "crh",    "cs",     "csb",   "cu",    "cv",    "cy",
-    "da",       "de",     "doi",    "dv",    "dz",    "ee",    "el",
-    "en",       "eo",     "es",     "et",    "eu",    "fa",    "fat",
-    "ff",       "fi",     "fil",    "fj",    "fo",    "fr",    "fur",
-    "fy",       "ga",     "gd",     "gez",   "gl",    "gn",    "gu",
-    "gv",       "ha",     "haw",    "he",    "hi",    "hne",   "ho",
-    "hr",       "hsb",    "ht",     "hu",    "hy",    "hz",    "ia",
-    "id",       "ie",     "ig",     "ii",    "ik",    "io",    "is",
-    "it",       "iu",     "ja",     "jv",    "ka",    "kaa",   "kab",
-    "ki",       "kj",     "kk",     "kl",    "km",    "kn",    "ko",
-    "kok",      "kr",     "ks",     "ku-am", "ku-iq", "ku-ir", "ku-tr",
-    "kum",      "kv",     "kw",     "kwm",   "ky",    "la",    "lah",
-    "lb",       "lez",    "lg",     "li",    "ln",    "lo",    "lt",
-    "lv",       "mai",    "mg",     "mh",    "mi",    "mk",    "ml",
-    "mn-cn",    "mn-mn",  "mni",    "mo",    "mr",    "ms",    "mt",
-    "my",       "na",     "nb",     "nds",   "ne",    "ng",    "nl",
-    "nn",       "no",     "nqo",    "nr",    "nso",   "nv",    "ny",
-    "oc",       "om",     "or",     "os",    "ota",   "pa",    "pa-pk",
-    "pap-an",   "pap-aw", "pl",     "ps-af", "ps-pk", "pt",    "qu",
-    "quz",      "rm",     "rn",     "ro",    "ru",    "rw",    "sa",
-    "sah",      "sat",    "sc",     "sco",   "sd",    "se",    "sel",
-    "sg",       "sh",     "shs",    "si",    "sid",   "sk",    "sl",
-    "sm",       "sma",    "smj",    "smn",   "sms",   "sn",    "so",
-    "sq",       "sr",     "ss",     "st",    "su",    "sv",    "sw",
-    "syr",      "ta",     "te",     "tg",    "th",    "ti-er", "ti-et",
-    "tig",      "tk",     "tl",     "tn",    "to",    "tr",    "ts",
-    "tt",       "tw",     "ty",     "tyv",   "ug",    "uk",    "und-zmth",
-    "und-zsye", "ur",     "uz",     "ve",    "vi",    "vo",    "vot",
-    "wa",       "wal",    "wen",    "wo",    "xh",    "yap",   "yi",
-    "yo",       "za",     "zh-cn",  "zh-hk", "zh-mo", "zh-sg", "zh-tw",
-    "zu"};
-}  // namespace
-
 class DateHelperUnittest : public AshTestBase {
  public:
   DateHelperUnittest() = default;
@@ -125,8 +84,8 @@
 
 // Tests getting the calendar week titles in all languages.
 TEST_F(DateHelperUnittest, GetWeekTitleForAllLocales) {
-  for (auto* local : kLocales) {
-    SetDefaultLocale(local);
+  for (auto* locale : kLocales) {
+    SetDefaultLocale(locale);
     EXPECT_EQ(7U, DateHelper::GetInstance()->week_titles().size());
   }
 }
diff --git a/ash/system/time/time_view.cc b/ash/system/time/time_view.cc
index c216253..720fe63 100644
--- a/ash/system/time/time_view.cc
+++ b/ash/system/time/time_view.cc
@@ -305,6 +305,7 @@
 
   switch (type_) {
     case kTime: {
+      // Calculate horizontal clock layout label.
       const std::u16string current_time =
           base::TimeFormatTimeOfDayWithHourClockType(
               now, model_->hour_clock_type(), base::kDropAmPm);
@@ -317,22 +318,19 @@
           ax::mojom::Event::kTextChanged, true);
 
       // Calculate vertical clock layout labels.
-      const size_t colon_pos = current_time.find(u":");
-      std::u16string hour = current_time.substr(0, colon_pos);
-      const std::u16string minute = current_time.substr(colon_pos + 1);
+      std::u16string current_hours =
+          (model_->hour_clock_type() == base::k24HourClock)
+              ? calendar_utils::GetTwentyFourHourClockHours(now)
+              : calendar_utils::GetTwelveHourClockHours(now);
+      const std::u16string current_minutes = calendar_utils::GetMinutes(now);
 
-      // Sometimes pad single-digit hours with a zero for aesthetic reasons.
-      if (hour.length() == 1 &&
-          model_->hour_clock_type() == base::k24HourClock &&
-          !base::i18n::IsRTL())
-        hour = u"0" + hour;
-
-      vertical_label_hours_->SetText(hour);
-      vertical_label_minutes_->SetText(minute);
+      vertical_label_hours_->SetText(current_hours);
+      vertical_label_minutes_->SetText(current_minutes);
       vertical_label_hours_->NotifyAccessibilityEvent(
           ax::mojom::Event::kTextChanged, true);
       vertical_label_minutes_->NotifyAccessibilityEvent(
           ax::mojom::Event::kTextChanged, true);
+
       Layout();
 
       // When the `new_label` text does not have the some length as the
diff --git a/ash/system/tray/system_nudge.cc b/ash/system/tray/system_nudge.cc
index a1ef011..1eb387fe 100644
--- a/ash/system/tray/system_nudge.cc
+++ b/ash/system/tray/system_nudge.cc
@@ -200,6 +200,9 @@
   const std::u16string accessibility_text = GetAccessibilityText();
   if (!accessibility_text.empty())
     nudge_view_->GetViewAccessibility().AnnounceText(accessibility_text);
+
+  base::UmaHistogramEnumeration("Ash.NotifierFramework.Nudge.ShownCount",
+                                params_.catalog_name);
 }
 
 void SystemNudge::Close() {
diff --git a/ash/system/tray/system_nudge_unittest.cc b/ash/system/tray/system_nudge_unittest.cc
index 99795012..57ad0a8 100644
--- a/ash/system/tray/system_nudge_unittest.cc
+++ b/ash/system/tray/system_nudge_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/system/tray/system_nudge_label.h"
 #include "ash/test/ash_test_base.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "ui/gfx/vector_icon_types.h"
 
 namespace ash {
@@ -26,9 +27,11 @@
 
 class TestSystemNudge : public SystemNudge {
  public:
-  explicit TestSystemNudge(bool anchor_status_area)
+  explicit TestSystemNudge(
+      bool anchor_status_area,
+      NudgeCatalogName catalog_name = NudgeCatalogName::kTestCatalogName)
       : SystemNudge(kNudgeName,
-                    NudgeCatalogName::kTestCatalogName,
+                    catalog_name,
                     kIconSize,
                     kIconLabelSpacing,
                     kNudgePadding,
@@ -50,6 +53,9 @@
   }
 };
 
+constexpr char kNudgeShownCountHistogramName[] =
+    "Ash.NotifierFramework.Nudge.ShownCount";
+
 }  // namespace
 
 using SystemNudgeTest = AshTestBase;
@@ -122,4 +128,22 @@
   EXPECT_EQ(nudge_bounds.bottom(), display_bounds.bottom());
 }
 
+TEST_F(SystemNudgeTest, ShownCountMetric) {
+  base::HistogramTester histogram_tester;
+
+  const NudgeCatalogName catalog_name_1 = static_cast<NudgeCatalogName>(1);
+  const NudgeCatalogName catalog_name_2 = static_cast<NudgeCatalogName>(2);
+  TestSystemNudge nudge_1(/*anchor_status_area=*/true, catalog_name_1);
+  TestSystemNudge nudge_2(/*anchor_status_area=*/true, catalog_name_2);
+
+  nudge_1.Show();
+  histogram_tester.ExpectBucketCount(kNudgeShownCountHistogramName,
+                                     catalog_name_1, 1);
+
+  nudge_2.Show();
+  nudge_2.Show();
+  histogram_tester.ExpectBucketCount(kNudgeShownCountHistogramName,
+                                     catalog_name_2, 2);
+}
+
 }  // namespace ash
diff --git a/ash/system/unified/deferred_update_dialog.cc b/ash/system/unified/deferred_update_dialog.cc
index 80dfd5af..6e08c79 100644
--- a/ash/system/unified/deferred_update_dialog.cc
+++ b/ash/system/unified/deferred_update_dialog.cc
@@ -27,12 +27,16 @@
   // dialog_ will be released when the dialog is closed.
   dialog_ = new DeferredUpdateDialog();
 
-  auto ok_text = callback_action == kSignOut
-                     ? IDS_DEFERRED_UPDATE_DIALOG_UPDATE_SIGN_OUT
-                     : IDS_DEFERRED_UPDATE_DIALOG_UPDATE_SHUT_DOWN;
-  auto cancel_text = callback_action == kSignOut
-                         ? IDS_DEFERRED_UPDATE_DIALOG_SIGN_OUT
-                         : IDS_DEFERRED_UPDATE_DIALOG_SHUT_DOWN;
+  auto ok_text = IDS_DEFERRED_UPDATE_DIALOG_UPDATE_SIGN_OUT;
+  auto cancel_text = IDS_DEFERRED_UPDATE_DIALOG_SIGN_OUT;
+
+  // Override texts for shutdown.
+  bool shutdown = callback_action == kShutDown;
+  if (shutdown) {
+    ok_text = IDS_DEFERRED_UPDATE_DIALOG_UPDATE_SHUT_DOWN;
+    cancel_text = IDS_DEFERRED_UPDATE_DIALOG_SHUT_DOWN;
+  }
+
   std::unique_ptr<ui::DialogModel> dialog_model =
       ui::DialogModel::Builder(std::make_unique<ui::DialogModelDelegate>())
           .SetTitle(l10n_util::GetStringUTF16(IDS_DEFERRED_UPDATE_DIALOG_TITLE))
@@ -49,9 +53,9 @@
           .AddCheckbox(kAutoUpdateCheckboxId,
                        ui::DialogModelLabel(l10n_util::GetStringUTF16(
                            IDS_DEFERRED_UPDATE_DIALOG_CHECKBOX)))
-          .SetDialogDestroyingCallback(
-              base::BindOnce(&DeferredUpdateDialog::OnDialogClosing,
-                             base::Unretained(dialog_), std::move(callback)))
+          .SetDialogDestroyingCallback(base::BindOnce(
+              &DeferredUpdateDialog::OnDialogClosing, base::Unretained(dialog_),
+              shutdown, std::move(callback)))
           .Build();
 
   dialog_->dialog_model_ = dialog_model.get();
@@ -84,7 +88,8 @@
 }
 
 // Invoked when the dialog is closing.
-void DeferredUpdateDialog::OnDialogClosing(base::OnceClosure callback) {
+void DeferredUpdateDialog::OnDialogClosing(bool shutdown_after_update,
+                                           base::OnceClosure callback) {
   dialog_model_ = nullptr;
 
   switch (dialog_result_) {
@@ -94,7 +99,8 @@
           /*enable=*/true);
       [[fallthrough]];
     case kApplyUpdate:
-      UpdateEngineClient::Get()->ApplyDeferredUpdate(std::move(callback));
+      UpdateEngineClient::Get()->ApplyDeferredUpdate(shutdown_after_update,
+                                                     std::move(callback));
       break;
     case kIgnoreUpdate:
       std::move(callback).Run();
diff --git a/ash/system/unified/deferred_update_dialog.h b/ash/system/unified/deferred_update_dialog.h
index 287186c..1fa58b2 100644
--- a/ash/system/unified/deferred_update_dialog.h
+++ b/ash/system/unified/deferred_update_dialog.h
@@ -43,7 +43,7 @@
   // Invoked when "cancel" button is clicked.
   void OnContinueWithoutUpdate();
   // Invoked when the dialog is closing.
-  void OnDialogClosing(base::OnceClosure callback);
+  void OnDialogClosing(bool shutdown_after_update, base::OnceClosure callback);
 
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAutoUpdateCheckboxId);
   static DeferredUpdateDialog* dialog_;
diff --git a/ash/system/update/update_notification_controller.cc b/ash/system/update/update_notification_controller.cc
index 307101b9..32ca20e 100644
--- a/ash/system/update/update_notification_controller.cc
+++ b/ash/system/update/update_notification_controller.cc
@@ -319,7 +319,8 @@
 
     if (ShouldShowDeferredUpdate()) {
       // When the "update" button is clicked, apply the deferred update.
-      ash::UpdateEngineClient::Get()->ApplyDeferredUpdate(base::DoNothing());
+      ash::UpdateEngineClient::Get()->ApplyDeferredUpdate(
+          /*shutdown_after_update=*/false, base::DoNothing());
     } else if (model_->update_required()) {
       // Restart
       if (slow_boot_file_path_exists_) {
diff --git a/ash/webui/help_app_ui/help_app_untrusted_ui.cc b/ash/webui/help_app_ui/help_app_untrusted_ui.cc
index ee58d6d..b53e4d2 100644
--- a/ash/webui/help_app_ui/help_app_untrusted_ui.cc
+++ b/ash/webui/help_app_ui/help_app_untrusted_ui.cc
@@ -36,7 +36,7 @@
   source->AddResourcePaths(base::make_span(
       kChromeosHelpAppBundleResources, kChromeosHelpAppBundleResourcesSize));
 
-  MaybeConfigureTestableDataSource(source);
+  MaybeConfigureTestableDataSource(source, "help_app/untrusted");
 
   // Add device and feature flags.
   populate_load_time_data_callback.Run(source);
diff --git a/ash/webui/media_app_ui/media_app_guest_ui.cc b/ash/webui/media_app_ui/media_app_guest_ui.cc
index 2b880ba..f4afee1 100644
--- a/ash/webui/media_app_ui/media_app_guest_ui.cc
+++ b/ash/webui/media_app_ui/media_app_guest_ui.cc
@@ -194,7 +194,8 @@
       CreateMediaAppUntrustedDataSource(web_ui, delegate);
 
   MaybeConfigureTestableDataSource(
-      untrusted_source, base::BindRepeating(&IsFontRequest),
+      untrusted_source, "media_app/untrusted",
+      base::BindRepeating(&IsFontRequest),
       base::BindRepeating(&MediaAppGuestUI::StartFontDataRequest,
                           weak_factory_.GetWeakPtr()));
 
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index b883e2e..820625f 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -1117,6 +1117,13 @@
 
 void ShimlessRmaService::ProvisioningProgress(
     const rmad::ProvisionStatus& status) {
+  if (status.status() ==
+          rmad::ProvisionStatus::RMAD_PROVISION_STATUS_FAILED_BLOCKING ||
+      status.status() ==
+          rmad::ProvisionStatus::RMAD_PROVISION_STATUS_FAILED_NON_BLOCKING) {
+    LOG(ERROR) << "Provisioning failed with error " << status.error();
+  }
+
   last_provisioning_progress_ = status;
   if (provisioning_observer_.is_bound()) {
     provisioning_observer_->OnProvisioningUpdated(
@@ -1149,6 +1156,13 @@
 
 void ShimlessRmaService::FinalizationProgress(
     const rmad::FinalizeStatus& status) {
+  if (status.status() ==
+          rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_FAILED_BLOCKING ||
+      status.status() ==
+          rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_FAILED_NON_BLOCKING) {
+    LOG(ERROR) << "Finalization failed with error " << status.error();
+  }
+
   last_finalization_progress_ = status;
   if (finalization_observer_.is_bound()) {
     finalization_observer_->OnFinalizationUpdated(
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.h b/ash/webui/shimless_rma/backend/shimless_rma_service.h
index 71c978a..16549b5 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.h
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.h
@@ -296,6 +296,10 @@
   // Used to validate mojo only states such as kConfigureNetwork
   mojom::State mojo_state_;
 
+  // These variables are used to save the most recent values of the
+  // corresponding variables, in case if the rmad client has already sent them,
+  // but the front end observer isn't connected yet. The value will be passed to
+  // the front end observer when it connects.
   absl::optional<rmad::CalibrationComponentStatus> last_calibration_progress_;
   absl::optional<rmad::CalibrationOverallStatus>
       last_calibration_overall_progress_;
diff --git a/ash/webui/shimless_rma/resources/wrapup_finalize_page.js b/ash/webui/shimless_rma/resources/wrapup_finalize_page.js
index 9cfd248..b63c3d2 100644
--- a/ash/webui/shimless_rma/resources/wrapup_finalize_page.js
+++ b/ash/webui/shimless_rma/resources/wrapup_finalize_page.js
@@ -63,7 +63,7 @@
     /** @private {ShimlessRmaServiceInterface} */
     this.shimlessRmaService_ = getShimlessRmaService();
     /**
-     * Receiver responsible for observing hardware write protection state.
+     * Receiver responsible for observing finalization progress and state.
      * @private {?FinalizationObserverReceiver}
      */
     this.finalizationObserverReceiver_ = new FinalizationObserverReceiver(
diff --git a/ash/webui/web_applications/BUILD.gn b/ash/webui/web_applications/BUILD.gn
index c1ae9baa..1a8c170 100644
--- a/ash/webui/web_applications/BUILD.gn
+++ b/ash/webui/web_applications/BUILD.gn
@@ -12,6 +12,7 @@
     "webui_test_prod_util.h",
   ]
   deps = [
+    "//ash/constants",
     "//base",
     "//base:i18n",
     "//content/public/browser",
diff --git a/ash/webui/web_applications/webui_test_prod_util.cc b/ash/webui/web_applications/webui_test_prod_util.cc
index 519c2b2e..74fba72 100644
--- a/ash/webui/web_applications/webui_test_prod_util.cc
+++ b/ash/webui/web_applications/webui_test_prod_util.cc
@@ -4,8 +4,16 @@
 
 #include "ash/webui/web_applications/webui_test_prod_util.h"
 
+#include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/no_destructor.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/thread_restrictions.h"
 #include "content/public/common/content_switches.h"
 
 using content::WebUIDataSource;
@@ -54,27 +62,75 @@
   }
 }
 
+// Determines whether, when attempting to load a path, we want to, instead of
+// using the regular handler, load it from a file on disk.
+bool ShouldLoadResponseFromDisk(const base::FilePath& root,
+                                const std::string& path) {
+  const base::FilePath expanded = root.Append(path);
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  const bool exists = base::PathExists(expanded);
+  if (exists) {
+    VLOG(1) << "Loading test data from " << expanded << " for " << path;
+  } else {
+    VLOG(1) << "Unable to load test data from " << expanded << " for " << path
+            << ", as the file doesn't exist.";
+  }
+  return exists;
+}
+
+void LoadFileFromDisk(const base::FilePath& path,
+                      content::WebUIDataSource::GotDataCallback callback) {
+  std::string result;
+  CHECK(base::ReadFileToString(path, &result));
+
+  std::move(callback).Run(new base::RefCountedBytes(
+      reinterpret_cast<const unsigned char*>(result.data()), result.size()));
+}
+
+void LoadResponseFromDisk(const base::FilePath& root,
+                          const std::string& path,
+                          content::WebUIDataSource::GotDataCallback callback) {
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(LoadFileFromDisk, root.Append(path), std::move(callback)));
+}
+
 }  // namespace
 
 bool MaybeConfigureTestableDataSource(
     WebUIDataSource* host_source,
+    const std::string& handler_name,
     const WebUIDataSource::ShouldHandleRequestCallback&
         real_should_handle_request_callback,
     const WebUIDataSource::HandleRequestCallback&
         real_handle_request_callback) {
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kTestType)) {
-    host_source->SetRequestFilter(real_should_handle_request_callback,
-                                  real_handle_request_callback);
-    return false;
+  const base::CommandLine& cmd = *base::CommandLine::ForCurrentProcess();
+  bool has_test_handler = false;
+  if (cmd.HasSwitch(::ash::switches::kWebUiDataSourcePathForTesting) &&
+      !handler_name.empty()) {
+    const base::FilePath root =
+        cmd.GetSwitchValuePath(::ash::switches::kWebUiDataSourcePathForTesting)
+            .Append(handler_name);
+    GetTestShouldHandleRequest() =
+        base::BindRepeating(ShouldLoadResponseFromDisk, root);
+    GetTestRequestFilterHandler() =
+        base::BindRepeating(LoadResponseFromDisk, root);
+    has_test_handler = true;
+  } else if (cmd.HasSwitch(::switches::kTestType)) {
+    has_test_handler = true;
   }
 
-  host_source->SetRequestFilter(
-      base::BindRepeating(&InvokeTestShouldHandleRequestCallback,
-                          real_should_handle_request_callback),
-      base::BindRepeating(&InvokeTestFileRequestFilterCallback,
-                          real_handle_request_callback));
-  return true;
+  if (has_test_handler) {
+    host_source->SetRequestFilter(
+        base::BindRepeating(&InvokeTestShouldHandleRequestCallback,
+                            real_should_handle_request_callback),
+        base::BindRepeating(&InvokeTestFileRequestFilterCallback,
+                            real_handle_request_callback));
+  } else {
+    host_source->SetRequestFilter(real_should_handle_request_callback,
+                                  real_handle_request_callback);
+  }
+  return has_test_handler;
 }
 
 void SetTestableDataSourceRequestHandlerForTesting(  // IN-TEST
diff --git a/ash/webui/web_applications/webui_test_prod_util.h b/ash/webui/web_applications/webui_test_prod_util.h
index 176a3bc..99b9919c 100644
--- a/ash/webui/web_applications/webui_test_prod_util.h
+++ b/ash/webui/web_applications/webui_test_prod_util.h
@@ -9,15 +9,23 @@
 
 // If the current process is running for tests, configures |host_source| for
 // testing by installing a request filter that can be satisfied by tests wanting
-// to provide custom resources. Note that if these resources are scripts,
-// further CSP changes (e.g. trusted-types) may be required in order for them to
-// load. If `real_[should_]handle_request_callback` arguments are provided, the
-// test request filter will be configured to handle these as well.
-// If not running for tests, `real_[should_]handle_request_callback` arguments
-// are passed directly to host_source->SetRequestFilter().
-// Returns true if the testing request filter was installed.
+// to provide custom resources.
+// If handler_name and the switch kWebUiDataSourcePathForTesting are provided,
+// then before attempting to load the real resource, it will first attempt to
+// load it from <switch_value>/handler_name.
+// For example, if the switch is /tmp/resource_overrides, attempting to load
+// js/app_main.js from the handler named "help_app/untrusted" will first
+// attempt to load from /tmp/resource_overrides/help_app/untrusted/js/main.js.
+// Note that if these resources are scripts, further CSP changes (e.g.
+// trusted-types) may be required in order for them to load. If
+// `real_[should_]handle_request_callback` arguments are provided, the test
+// request filter will be configured to handle these as well. If not running for
+// tests, `real_[should_]handle_request_callback` arguments are passed directly
+// to host_source->SetRequestFilter(). Returns true if the testing request
+// filter was installed.
 bool MaybeConfigureTestableDataSource(
     content::WebUIDataSource* host_source,
+    const std::string& handler_name = "",
     const content::WebUIDataSource::ShouldHandleRequestCallback&
         real_should_handle_request_callback = {},
     const content::WebUIDataSource::HandleRequestCallback&
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 297d3e16..c6eed2be 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -279,9 +279,9 @@
 namespace internal {
 
 namespace {
-constexpr size_t kMaxPurgeableSlotsPerSystemPage = 2;
+constexpr size_t kMaxPurgeableSlotsPerSystemPage = 64;
 PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE size_t
-MaxPurgeableSlotSize() {
+MinPurgeableSlotSize() {
   return SystemPageSize() / kMaxPurgeableSlotsPerSystemPage;
 }
 }  // namespace
@@ -297,7 +297,7 @@
   // We will do nothing if slot_size is smaller than SystemPageSize() / 2
   // because |kMaxSlotCount| will be too large in that case, which leads to
   // |slot_usage| using up too much memory.
-  if (slot_size < MaxPurgeableSlotSize() || !slot_span->num_allocated_slots)
+  if (slot_size < MinPurgeableSlotSize() || !slot_span->num_allocated_slots)
     return 0;
 
   size_t bucket_num_slots = bucket->get_slots_per_span();
@@ -320,7 +320,7 @@
 #if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR)
   constexpr size_t kMaxSlotCount =
       (PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) /
-      MaxPurgeableSlotSize();
+      MinPurgeableSlotSize();
 #elif BUILDFLAG(IS_APPLE) || (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
   // It's better for slot_usage to be stack-allocated and fixed-size, which
   // demands that its size be constexpr. On IS_APPLE and Linux on arm64,
@@ -332,7 +332,7 @@
       internal::kMaxPartitionPagesPerRegularSlotSpan;
   PA_CHECK(kMaxSlotCount == (PartitionPageSize() *
                              internal::kMaxPartitionPagesPerRegularSlotSpan) /
-                                MaxPurgeableSlotSize());
+                                MinPurgeableSlotSize());
 #endif
   PA_DCHECK(bucket_num_slots <= kMaxSlotCount);
   PA_DCHECK(slot_span->num_unprovisioned_slots < bucket_num_slots);
@@ -384,15 +384,15 @@
     // The slots that do not contain discarded pages should not be included to
     // |truncated_slots|. Detects those slots and fixes |truncated_slots| and
     // |num_slots| accordingly.
-    uintptr_t rounded_up_begin_addr = RoundUpToSystemPage(begin_addr);
-    for (size_t i = 0; i < kMaxPurgeableSlotsPerSystemPage; ++i) {
+    uintptr_t rounded_up_truncatation_begin_addr =
+        RoundUpToSystemPage(begin_addr);
+    while (begin_addr + slot_size <= rounded_up_truncatation_begin_addr) {
       begin_addr += slot_size;
-      if (RoundUpToSystemPage(begin_addr) != rounded_up_begin_addr)
-        break;
+      PA_DCHECK(truncated_slots);
       --truncated_slots;
       ++num_slots;
     }
-    begin_addr = rounded_up_begin_addr;
+    begin_addr = rounded_up_truncatation_begin_addr;
 
     // We round the end address here up and not down because we're at the end of
     // a slot span, so we "own" all the way up the page boundary.
@@ -442,6 +442,8 @@
   }
 
   if (slot_size < SystemPageSize()) {
+    // Returns here because implementing the following steps for smaller slot
+    // size will need a complicated logic and make the code messy.
     return discardable_bytes;
   }
 
@@ -1145,7 +1147,7 @@
         if (bucket.slot_size == internal::kInvalidBucketSize)
           continue;
 
-        if (bucket.slot_size >= internal::MaxPurgeableSlotSize())
+        if (bucket.slot_size >= internal::MinPurgeableSlotSize())
           internal::PartitionPurgeBucket(&bucket);
         else
           bucket.SortSlotSpanFreelists();
diff --git a/build/config/fuchsia/test/README.md b/build/config/fuchsia/test/README.md
index 3faeac9f09..af6143d 100644
--- a/build/config/fuchsia/test/README.md
+++ b/build/config/fuchsia/test/README.md
@@ -25,7 +25,7 @@
 For tests that use the fonts in `//third_party/test_fonts` by way of
 `//skia:test_fonts_cfv2`.
 
-#### jit_capabilities.test-cmx
+#### jit_capabilities.test-cmx and mark_vmo_executable.shard.test-cml
 Required by tests that execute JavaScript. Should only be required in a small
 number of tests.
 
diff --git a/build/config/fuchsia/test/jit_capabilities.test-cmx b/build/config/fuchsia/test/jit_capabilities.test-cmx
index ff70e25..97b4979 100644
--- a/build/config/fuchsia/test/jit_capabilities.test-cmx
+++ b/build/config/fuchsia/test/jit_capabilities.test-cmx
@@ -2,6 +2,9 @@
   "sandbox": {
     "features": [
       "deprecated-ambient-replace-as-executable"
+    ],
+    "services": [
+      "fuchsia.kernel.VmexResource"
     ]
   }
 }
diff --git a/build/config/fuchsia/test/mark_vmo_executable.shard.test-cml b/build/config/fuchsia/test/mark_vmo_executable.shard.test-cml
new file mode 100644
index 0000000..79a3dee
--- /dev/null
+++ b/build/config/fuchsia/test/mark_vmo_executable.shard.test-cml
@@ -0,0 +1,9 @@
+{
+  use: [
+    {
+      protocol: [
+        "fuchsia.kernel.VmexResource",
+      ],
+    },
+  ],
+}
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index b4f1e1fc..d40ee1b 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-9.20220815.3.1
+9.20220819.0.1
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index d8b4ab2e..ff11c18 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -911,6 +911,7 @@
 
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
   }
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE
index acef1f5..69502181 100644
--- a/chrome/MAJOR_BRANCH_DATE
+++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@
-MAJOR_BRANCH_DATE=2022-07-22
+MAJOR_BRANCH_DATE=2022-08-19
diff --git a/chrome/VERSION b/chrome/VERSION
index dfc0cee..f30e4c9f 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
-MAJOR=106
+MAJOR=107
 MINOR=0
-BUILD=5249
+BUILD=5250
 PATCH=0
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index aebe5cf..ede0912 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -451,6 +451,7 @@
     }
 
     private void enforceStateProperties(@KeyboardExtensionState int extensionState) {
+        TraceEvent.begin("ManualFillingMediator#enforceStateProperties");
         if (requiresVisibleBar(extensionState)) {
             mKeyboardAccessory.show();
         } else {
@@ -474,6 +475,7 @@
                 compositorViewHolderSupplier.get().requestLayout();
             }
         }
+        TraceEvent.end("ManualFillingMediator#enforceStateProperties");
     }
 
     private void updateKeyboard(@KeyboardExtensionState int extensionState) {
@@ -650,12 +652,14 @@
 
     private void refreshTabs() {
         if (!isInitialized()) return;
+        TraceEvent.begin("ManualFillingMediator#refreshTabs");
         ManualFillingState state = mStateCache.getStateFor(mActivity.getCurrentWebContents());
         state.notifyObservers();
         KeyboardAccessoryData.Tab[] tabs = state.getTabs();
         mAccessorySheet.setTabs(tabs); // Set the sheet tabs first to invalidate the tabs properly.
         mKeyboardAccessory.setTabs(tabs);
         state.requestRecentSheets();
+        TraceEvent.end("ManualFillingMediator#refreshTabs");
     }
 
     @VisibleForTesting
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
index 6e37d68b..fcde0cbc 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
@@ -14,6 +14,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.viewpager.widget.ViewPager;
 
+import org.chromium.base.TraceEvent;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.keyboard_accessory.AccessoryTabType;
 import org.chromium.chrome.browser.keyboard_accessory.bar_component.KeyboardAccessoryProperties.BarItem;
@@ -225,7 +226,9 @@
      * Triggers the accessory to be shown.
      */
     public void show() {
+        TraceEvent.begin("KeyboardAccessoryCoordinator#show");
         mMediator.show();
+        TraceEvent.end("KeyboardAccessoryCoordinator#show");
     }
 
     /** Next time the accessory is closed, don't delay the closing animation. */
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
index 0da6065..272f8db 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryMediator.java
@@ -20,6 +20,7 @@
 import androidx.annotation.Px;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.TraceEvent;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.keyboard_accessory.AccessoryAction;
 import org.chromium.chrome.browser.keyboard_accessory.AccessorySheetTrigger;
@@ -103,6 +104,7 @@
     @Override
     public void onItemAvailable(
             @AccessoryAction int typeId, KeyboardAccessoryData.Action[] actions) {
+        TraceEvent.begin("KeyboardAccessoryMediator#onItemAvailable");
         assert typeId != DEFAULT_TYPE : "Did not specify which Action type has been updated.";
         List<BarItem> retainedItems = collectItemsToRetain(typeId);
         retainedItems.addAll(0, toBarItems(actions));
@@ -110,6 +112,7 @@
             retainedItems.add(retainedItems.size(), mModel.get(TAB_LAYOUT_ITEM));
         }
         mModel.get(BAR_ITEMS).set(retainedItems);
+        TraceEvent.end("KeyboardAccessoryMediator#onItemAvailable");
     }
 
     private List<BarItem> collectItemsToRetain(@AccessoryAction int actionType) {
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetCoordinator.java
index 1b0c69aa..2fcb10c 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetCoordinator.java
@@ -21,6 +21,7 @@
 import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 
+import org.chromium.base.TraceEvent;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
 import org.chromium.ui.DeferredViewStubInflationProvider;
 import org.chromium.ui.ViewProvider;
@@ -120,7 +121,9 @@
      * Shows the Accessory Sheet.
      */
     public void show() {
+        TraceEvent.begin("AccessorySheetCoordinator#show");
         mMediator.show();
+        TraceEvent.end("AccessorySheetCoordinator#show");
     }
 
     /**
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMediator.java
index 3f21d82f..228d6139 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabMediator.java
@@ -7,6 +7,7 @@
 import androidx.annotation.CallSuper;
 import androidx.annotation.Nullable;
 
+import org.chromium.base.TraceEvent;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.keyboard_accessory.AccessoryAction;
 import org.chromium.chrome.browser.keyboard_accessory.AccessoryTabType;
@@ -56,7 +57,9 @@
 
     @Override
     public void onItemAvailable(int typeId, AccessorySheetData accessorySheetData) {
+        TraceEvent.begin("AccessorySheetTabMediator#onItemAvailable");
         mModel.set(splitIntoDataPieces(accessorySheetData));
+        TraceEvent.end("AccessorySheetTabMediator#onItemAvailable");
     }
 
     AccessorySheetTabMediator(AccessorySheetTabModel model, @AccessoryTabType int tabType,
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index a4deefb..c852711 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-106.0.5241.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-106.0.5249.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome.cml b/chrome/app/chrome.cml
index 4ef8412..8122df7 100644
--- a/chrome/app/chrome.cml
+++ b/chrome/app/chrome.cml
@@ -58,6 +58,7 @@
                 "fuchsia.hwinfo.Product",
                 "fuchsia.input.virtualkeyboard.ControllerCreator",
                 "fuchsia.intl.PropertyProvider",
+                "fuchsia.kernel.VmexResource",
                 "fuchsia.media.Audio",
                 "fuchsia.media.AudioDeviceEnumerator",
                 "fuchsia.media.ProfileProvider",
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index db192b3..8fb3178 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1850,7 +1850,7 @@
   <message name="IDS_SETTINGS_CROSTINI_PRECONFIGURED_CONTAINERS_ATTACH_PLAYBOOK_BUTTON_LABEL" desc="Label for button to attach an Ansible Playbook to preconfigure a Linux container.">
     Select playbook
   </message>
-  <message name="IDS_SETTINGS_CROSTINI_ANSIBLE_PLAYBOOK_SELECT_DIALOG_TITLE" desc="The text used as the title for Ansible Playbook selection">
+  <message name="IDS_SETTINGS_CROSTINI_FILE_SELECTOR_DIALOG_TITLE" desc="The text used as the title for selecting a file for Crostini container creation">
     Select an Ansible Playbook to add
   </message>
   <message name="IDS_SETTINGS_CROSTINI_DISK_RESIZE_SHOW_BUTTON" desc="The label of the button/link the user clicks to open the disk resizing dialogue." meaning="The label of the button/link the user clicks to open the disk resizing dialogue.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_ANSIBLE_PLAYBOOK_SELECT_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_ANSIBLE_PLAYBOOK_SELECT_DIALOG_TITLE.png.sha1
deleted file mode 100644
index 7e0f1f2..0000000
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_ANSIBLE_PLAYBOOK_SELECT_DIALOG_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-89f094df5a0b0aa30d170cc976b85ae0e26c5bad
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_FILE_SELECTOR_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_FILE_SELECTOR_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..a460627
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_FILE_SELECTOR_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+a46bcebc79414f0330f60c36a0c71788259b1365
\ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp
index db91b40c..a194df5 100644
--- a/chrome/app/password_manager_ui_strings.grdp
+++ b/chrome/app/password_manager_ui_strings.grdp
@@ -3,4 +3,7 @@
   <message name="IDS_PASSWORD_MANAGER_UI_TITLE" desc="Title of the Password Manager page">
     Password Manager
   </message>
+  <message name="IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT" desc="Placeholder in the search field.">
+    Search passwords
+  </message>
 </grit-part>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT.png.sha1
new file mode 100644
index 0000000..78a433f
--- /dev/null
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT.png.sha1
@@ -0,0 +1 @@
+749329bb7695a1158c9e1ea6a515f4e61d9a64e9
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 10cf1f6..1894cb67 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2962,6 +2962,12 @@
   <message name="IDS_SETTINGS_COOKIES_BLOCK_ALL_BULLET_THREE" desc="Description for the third bullet of block all cookies radio toggle">
     Features on many sites may not work
   </message>
+  <message name="IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_LABEL" desc="Label for the toggle that allows the user to control First Party sets enabled sharing between related sites" translateable="false">
+    Allow related sites to remember you across sites
+  </message>
+  <message name="IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_SUB_LABEL" desc="Sub-label for the toggle that allows the user to control First Party sets enabled sharing between related sites" translateable="false">
+    Related sites use cookies to help with things like keeping you signed in
+  </message>
   <message name="IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT" desc="Label for the toggle that allows the user to automatically delete their cookies and site data when they close all browser windows.">
     Clear cookies and site data when you close all windows
   </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e5d03e2..809b7ad 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4350,6 +4350,9 @@
     {"bruschetta", flag_descriptions::kBruschettaName,
      flag_descriptions::kBruschettaDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kBruschetta)},
+    {"bruschetta-alpha-migrate", flag_descriptions::kBruschettaAlphaMigrateName,
+     flag_descriptions::kBruschettaAlphaMigrateDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kBruschettaAlphaMigrate)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #if (BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || \
      BUILDFLAG(IS_ANDROID)) &&                        \
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 5a1dbe89..6f14fe9 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -769,8 +769,6 @@
     "chrome_browser_main_parts_ash.h",
     "concierge_helper_service.cc",
     "concierge_helper_service.h",
-    "crostini/ansible/ansible_file_selector.cc",
-    "crostini/ansible/ansible_file_selector.h",
     "crostini/ansible/ansible_management_service.cc",
     "crostini/ansible/ansible_management_service.h",
     "crostini/ansible/ansible_management_service_factory.cc",
@@ -787,6 +785,8 @@
     "crostini/crostini_export_import_status_tracker.h",
     "crostini/crostini_features.cc",
     "crostini/crostini_features.h",
+    "crostini/crostini_file_selector.cc",
+    "crostini/crostini_file_selector.h",
     "crostini/crostini_force_close_watcher.cc",
     "crostini/crostini_force_close_watcher.h",
     "crostini/crostini_installer.cc",
diff --git a/chrome/browser/ash/bruschetta/bruschetta_mount_provider_unittest.cc b/chrome/browser/ash/bruschetta/bruschetta_mount_provider_unittest.cc
index 41ef707..a44b5e4 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_mount_provider_unittest.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_mount_provider_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/test/bind.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_launcher.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_service.h"
+#include "chrome/browser/ash/bruschetta/bruschetta_service_factory.h"
 #include "chrome/browser/ash/bruschetta/fake_bruschetta_launcher.h"
 #include "chrome/browser/ash/guest_os/guest_id.h"
 #include "chrome/browser/ash/guest_os/guest_os_session_tracker.h"
@@ -21,6 +22,7 @@
 class BruschettaMountProviderTest : public testing::Test {
  protected:
   BruschettaMountProviderTest() {
+    BruschettaServiceFactory::EnableForTesting(&profile_);
     BruschettaMountProvider provider{&profile_, id_};
 
     guest_os::GuestOsSessionTracker::GetForProfile(&profile_)
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service.cc b/chrome/browser/ash/bruschetta/bruschetta_service.cc
index e0c664e..ce35b13 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_service.cc
@@ -5,35 +5,42 @@
 #include "chrome/browser/ash/bruschetta/bruschetta_service.h"
 
 #include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "bruschetta_terminal_provider.h"
+#include "chrome/browser/ash/bruschetta/bruschetta_features.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_launcher.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_mount_provider.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_service_factory.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_util.h"
-#include "chrome/browser/ash/guest_os/guest_id.h"
 #include "chrome/browser/ash/guest_os/public/guest_os_service.h"
 #include "chrome/browser/ash/guest_os/public/types.h"
 
 namespace bruschetta {
 
-BruschettaService::BruschettaService(Profile* profile) {
-  // TODO(b/233289313): Once we have an installer we need to do this
-  // dynamically, but in the alpha people have VMs they created via vmc called
-  // "bru" so hardcode this to get them working while we work on the full
-  // installer. Similarly, Bruschetta doesn't have a container but it runs a
-  // garcon that identifies itself as in a penguin container, so use that name.
-  guest_os::GuestId alpha_id{guest_os::VmType::BRUSCHETTA, kBruschettaVmName,
-                             "penguin"};
-  launchers_.insert(
-      {alpha_id.vm_name, std::make_unique<BruschettaLauncher>("bru", profile)});
-  guest_os::GuestOsService::GetForProfile(profile)
-      ->MountProviderRegistry()
-      ->Register(std::make_unique<BruschettaMountProvider>(profile, alpha_id));
-  guest_os::GuestOsService::GetForProfile(profile)
-      ->TerminalProviderRegistry()
-      ->Register(
-          std::make_unique<BruschettaTerminalProvider>(profile, alpha_id));
+BruschettaService::BruschettaService(Profile* profile) : profile_(profile) {
+  // Don't set up anything if the bruschetta flag isn't enabled.
+  if (!BruschettaFeatures::Get()->IsEnabled())
+    return;
+
+  bool registered_guests = false;
+  // Register all bruschetta instances that have already been installed.
+  for (const auto& guest_id :
+       guest_os::GetContainers(profile, guest_os::VmType::BRUSCHETTA)) {
+    Register(guest_id);
+    registered_guests = true;
+  }
+
+  // Migrate VMs installed during the alpha. These will have been set up by hand
+  // using vmc so chrome doesn't know about this, but we know what the VM name
+  // should be, so register it here is nothing has been registered from prefs
+  // and the migration flag is turned on.
+  if (!registered_guests && base::FeatureList::IsEnabled(
+                                chromeos::features::kBruschettaAlphaMigrate)) {
+    Register(GetBruschettaId());
+  }
 }
 
 BruschettaService::~BruschettaService() = default;
@@ -42,6 +49,18 @@
   return BruschettaServiceFactory::GetForProfile(profile);
 }
 
+void BruschettaService::Register(const guest_os::GuestId& guest_id) {
+  launchers_.insert({guest_id.vm_name, std::make_unique<BruschettaLauncher>(
+                                           guest_id.vm_name, profile_)});
+  guest_os::GuestOsService::GetForProfile(profile_)
+      ->MountProviderRegistry()
+      ->Register(std::make_unique<BruschettaMountProvider>(profile_, guest_id));
+  guest_os::GuestOsService::GetForProfile(profile_)
+      ->TerminalProviderRegistry()
+      ->Register(
+          std::make_unique<BruschettaTerminalProvider>(profile_, guest_id));
+}
+
 base::WeakPtr<BruschettaLauncher> BruschettaService::GetLauncher(
     std::string vm_name) {
   auto it = launchers_.find(vm_name);
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service.h b/chrome/browser/ash/bruschetta/bruschetta_service.h
index 953d91d..b160b3fa 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service.h
+++ b/chrome/browser/ash/bruschetta/bruschetta_service.h
@@ -7,6 +7,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/ash/guest_os/guest_id.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 class Profile;
@@ -26,6 +27,11 @@
   // Helper method to get the service instance for the given profile.
   static BruschettaService* GetForProfile(Profile* profile);
 
+  // Register a bruschetta instance with the GuestOS service. This is called at
+  // service construction for all installed instances, and from the installer
+  // for new instances.
+  void Register(const guest_os::GuestId& guest_id);
+
   // Returns a handle to the launcher for the vm specified by `vm_name`. Will
   // return a null pointer if the name isn't recognised.
   base::WeakPtr<BruschettaLauncher> GetLauncher(std::string vm_name);
@@ -35,6 +41,8 @@
 
  private:
   base::flat_map<std::string, std::unique_ptr<BruschettaLauncher>> launchers_;
+
+  Profile* const profile_;
 };
 
 }  // namespace bruschetta
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service_factory.cc b/chrome/browser/ash/bruschetta/bruschetta_service_factory.cc
index 37db726d..08b8354 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service_factory.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_service_factory.cc
@@ -4,7 +4,10 @@
 
 #include "chrome/browser/ash/bruschetta/bruschetta_service_factory.h"
 
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_service.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 
 namespace bruschetta {
@@ -29,7 +32,44 @@
 
 KeyedService* BruschettaServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new BruschettaService(Profile::FromBrowserContext(context));
+  auto* profile = Profile::FromBrowserContext(context);
+
+  // Don't create a BruschettaService for anything except the primary user
+  // profile for the session.
+  if (profile->IsOffTheRecord() ||
+      !ash::ProfileHelper::Get()->IsRegularProfile(profile) ||
+      !ash::ProfileHelper::Get()->IsPrimaryProfile(profile) ||
+      ash::ProfileHelper::Get()->IsEphemeralUserProfile(profile))
+    return nullptr;
+
+  return new BruschettaService(profile);
+}
+
+// Force BruschettaService to be set up when a BrowserContext is
+// initialized. This lets us do set up that should happen at the start of the
+// session (e.g. registering existing Bruschetta VMs with other services).
+bool BruschettaServiceFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+// Most tests don't set up the dependencies (e.g. user_manager::UserManager) for
+// the ash::ProfileHelper calls in ::BuildServiceInstanceFor we use to check if
+// we should create a BruschettaService for a given BrowserContext. Since they
+// probably don't actually want a BruschettaService anyway, we use this to
+// default to not creating one for test BrowserContexts.
+bool BruschettaServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
+// Static helper function for tests that do use BruschettaService. This bypasses
+// the checks we normally use, so it doesn't introduce a dependency on
+// user_manager::UserManager etc.
+void BruschettaServiceFactory::EnableForTesting(Profile* profile) {
+  GetInstance()->SetTestingFactory(
+      profile, base::BindRepeating([](content::BrowserContext* context) {
+        return base::WrapUnique<KeyedService>(
+            new BruschettaService(Profile::FromBrowserContext(context)));
+      }));
 }
 
 }  // namespace bruschetta
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service_factory.h b/chrome/browser/ash/bruschetta/bruschetta_service_factory.h
index 0399997b..354112c4 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service_factory.h
+++ b/chrome/browser/ash/bruschetta/bruschetta_service_factory.h
@@ -18,6 +18,7 @@
  public:
   static bruschetta::BruschettaService* GetForProfile(Profile* profile);
   static BruschettaServiceFactory* GetInstance();
+  static void EnableForTesting(Profile* profile);
 
   BruschettaServiceFactory(const BruschettaServiceFactory&) = delete;
   BruschettaServiceFactory& operator=(const BruschettaServiceFactory&) = delete;
@@ -31,6 +32,9 @@
   // BrowserContextKeyedServiceFactory:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* context) const override;
+
+  bool ServiceIsCreatedWithBrowserContext() const override;
+  bool ServiceIsNULLWhileTesting() const override;
 };
 
 }  // namespace bruschetta
diff --git a/chrome/browser/ash/bruschetta/bruschetta_service_unittest.cc b/chrome/browser/ash/bruschetta/bruschetta_service_unittest.cc
index cbe8c04..4359d8c 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_service_unittest.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_service_unittest.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/bruschetta/bruschetta_service.h"
+#include "ash/constants/ash_features.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/bruschetta/bruschetta_util.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -19,11 +22,13 @@
 
  protected:
   void SetUp() override {
+    feature_list_.InitAndEnableFeature(ash::features::kBruschetta);
     service_ = std::make_unique<BruschettaService>(&profile_);
   }
 
   void TearDown() override {}
 
+  base::test::ScopedFeatureList feature_list_;
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile_;
   std::unique_ptr<BruschettaService> service_;
@@ -31,6 +36,7 @@
 
 // GetLauncher returns launcher.
 TEST_F(BruschettaServiceTest, GetLauncher) {
+  service_->Register(GetBruschettaId());
   ASSERT_NE(service_->GetLauncher("bru"), nullptr);
 }
 
diff --git a/chrome/browser/ash/bruschetta/bruschetta_terminal_provider_unittest.cc b/chrome/browser/ash/bruschetta/bruschetta_terminal_provider_unittest.cc
index b2e44e9..f8c3649 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_terminal_provider_unittest.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_terminal_provider_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/bind.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_launcher.h"
 #include "chrome/browser/ash/bruschetta/bruschetta_service.h"
+#include "chrome/browser/ash/bruschetta/bruschetta_service_factory.h"
 #include "chrome/browser/ash/guest_os/guest_id.h"
 #include "chrome/browser/ash/guest_os/public/types.h"
 #include "chrome/browser/extensions/api/terminal/startup_status.h"
@@ -22,6 +23,7 @@
 class BruschettaTerminalProviderTest : public testing::Test {
  public:
   BruschettaTerminalProviderTest() {
+    BruschettaServiceFactory::EnableForTesting(&profile_);
     std::unique_ptr<FakeBruschettaLauncher> launcher =
         std::make_unique<FakeBruschettaLauncher>();
     launcher_ = launcher.get();
diff --git a/chrome/browser/ash/bruschetta/bruschetta_util.cc b/chrome/browser/ash/bruschetta/bruschetta_util.cc
index bb1bde6..bb1b5c56 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_util.cc
+++ b/chrome/browser/ash/bruschetta/bruschetta_util.cc
@@ -25,4 +25,9 @@
   return "unknown code";
 }
 
+guest_os::GuestId GetBruschettaId() {
+  return guest_os::GuestId{guest_os::VmType::BRUSCHETTA, kBruschettaVmName,
+                           "penguin"};
+}
+
 }  // namespace bruschetta
diff --git a/chrome/browser/ash/bruschetta/bruschetta_util.h b/chrome/browser/ash/bruschetta/bruschetta_util.h
index 7a50bfa..d542f54 100644
--- a/chrome/browser/ash/bruschetta/bruschetta_util.h
+++ b/chrome/browser/ash/bruschetta/bruschetta_util.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_UTIL_H_
 #define CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_UTIL_H_
 
+#include "chrome/browser/ash/guest_os/guest_id.h"
+
 namespace bruschetta {
 
 extern const char kBruschettaVmName[];
@@ -22,6 +24,8 @@
 // Returns the string name of the BruschettaResult.
 const char* BruschettaResultString(const BruschettaResult res);
 
+guest_os::GuestId GetBruschettaId();
+
 }  // namespace bruschetta
 
 #endif  // CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_UTIL_H_
diff --git a/chrome/browser/ash/crostini/ansible/ansible_file_selector.cc b/chrome/browser/ash/crostini/crostini_file_selector.cc
similarity index 67%
rename from chrome/browser/ash/crostini/ansible/ansible_file_selector.cc
rename to chrome/browser/ash/crostini/crostini_file_selector.cc
index a539f0c..4ae59419 100644
--- a/chrome/browser/ash/crostini/ansible/ansible_file_selector.cc
+++ b/chrome/browser/ash/crostini/crostini_file_selector.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/ash/crostini/ansible/ansible_file_selector.h"
+#include "chrome/browser/ash/crostini/crostini_file_selector.h"
 
 #include "base/path_service.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -13,25 +13,27 @@
 #include "ui/base/l10n/l10n_util.h"
 
 namespace {
-ui::SelectFileDialog::FileTypeInfo GetAnsibleFileTypeInfo() {
+ui::SelectFileDialog::FileTypeInfo GetFileTypeInfo() {
   ui::SelectFileDialog::FileTypeInfo file_type_info;
   file_type_info.extensions.resize(1);
-  file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("yaml"));
+
+  // Allow Ansible playbooks (yaml) only.
+  file_type_info.extensions = {{"yaml"}};
 
   return file_type_info;
 }
 }  // namespace
 
 namespace crostini {
-AnsibleFileSelector::AnsibleFileSelector(content::WebUI* web_ui)
+CrostiniFileSelector::CrostiniFileSelector(content::WebUI* web_ui)
     : web_ui_(web_ui) {}
 
-AnsibleFileSelector::~AnsibleFileSelector() {
+CrostiniFileSelector::~CrostiniFileSelector() {
   if (select_file_dialog_.get())
     select_file_dialog_->ListenerDestroyed();
 }
 
-void AnsibleFileSelector::SelectFile(
+void CrostiniFileSelector::SelectFile(
     base::OnceCallback<void(const base::FilePath&)> selected_callback,
     base::OnceCallback<void(void)> cancelled_callback) {
   selected_callback_ = std::move(selected_callback);
@@ -44,32 +46,35 @@
   // uploaded Ansible Playbook if the user has already "uploaded" a playbook
   // before.
   base::FilePath downloads_path;
-  if (!base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path))
+  if (!base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) {
+    LOG(ERROR) << "Default Downloads path does not exist, cannot open file "
+                  "selector for create container";
     return;
+  }
 
-  ui::SelectFileDialog::FileTypeInfo file_type_info(GetAnsibleFileTypeInfo());
+  ui::SelectFileDialog::FileTypeInfo file_type_info(GetFileTypeInfo());
   select_file_dialog_->SelectFile(
       ui::SelectFileDialog::SELECT_OPEN_FILE,
       l10n_util::GetStringUTF16(
-          IDS_SETTINGS_CROSTINI_ANSIBLE_PLAYBOOK_SELECT_DIALOG_TITLE),
+          IDS_SETTINGS_CROSTINI_FILE_SELECTOR_DIALOG_TITLE),
       downloads_path, &file_type_info, 0, FILE_PATH_LITERAL(""),
       GetBrowserWindow(), NULL);
 }
 
-gfx::NativeWindow AnsibleFileSelector::GetBrowserWindow() {
+gfx::NativeWindow CrostiniFileSelector::GetBrowserWindow() {
   Browser* browser =
       chrome::FindBrowserWithWebContents(web_ui_->GetWebContents());
   return browser ? browser->window()->GetNativeWindow()
                  : gfx::kNullNativeWindow;
 }
 
-void AnsibleFileSelector::FileSelected(const base::FilePath& path,
-                                       int index,
-                                       void* params) {
+void CrostiniFileSelector::FileSelected(const base::FilePath& path,
+                                        int index,
+                                        void* params) {
   std::move(selected_callback_).Run(path);
 }
 
-void AnsibleFileSelector::FileSelectionCanceled(void* params) {
+void CrostiniFileSelector::FileSelectionCanceled(void* params) {
   if (cancelled_callback_)
     std::move(cancelled_callback_).Run();
 }
diff --git a/chrome/browser/ash/crostini/ansible/ansible_file_selector.h b/chrome/browser/ash/crostini/crostini_file_selector.h
similarity index 65%
rename from chrome/browser/ash/crostini/ansible/ansible_file_selector.h
rename to chrome/browser/ash/crostini/crostini_file_selector.h
index c4d2aa547..4c80f88 100644
--- a/chrome/browser/ash/crostini/ansible/ansible_file_selector.h
+++ b/chrome/browser/ash/crostini/crostini_file_selector.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ASH_CROSTINI_ANSIBLE_ANSIBLE_FILE_SELECTOR_H_
-#define CHROME_BROWSER_ASH_CROSTINI_ANSIBLE_ANSIBLE_FILE_SELECTOR_H_
+#ifndef CHROME_BROWSER_ASH_CROSTINI_CROSTINI_FILE_SELECTOR_H_
+#define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_FILE_SELECTOR_H_
 
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
@@ -13,15 +13,15 @@
 
 namespace crostini {
 
-// Helper class to open a file dialog to select an Ansible Playbook for
-// preconfigured containers.
-class AnsibleFileSelector : public ui::SelectFileDialog::Listener {
+// Helper class to open a file dialog to select a file from which the
+// container will be created.
+class CrostiniFileSelector : public ui::SelectFileDialog::Listener {
  public:
-  explicit AnsibleFileSelector(content::WebUI* web_ui);
-  AnsibleFileSelector(const AnsibleFileSelector&) = delete;
-  AnsibleFileSelector& operator=(const AnsibleFileSelector&) = delete;
+  explicit CrostiniFileSelector(content::WebUI* web_ui);
+  CrostiniFileSelector(const CrostiniFileSelector&) = delete;
+  CrostiniFileSelector& operator=(const CrostiniFileSelector&) = delete;
 
-  ~AnsibleFileSelector() override;
+  ~CrostiniFileSelector() override;
 
   // Opens a file selection dialog to choose an Ansible Playbook.
   virtual void SelectFile(
@@ -46,4 +46,4 @@
 
 }  // namespace crostini
 
-#endif  // CHROME_BROWSER_ASH_CROSTINI_ANSIBLE_ANSIBLE_FILE_SELECTOR_H_
+#endif  // CHROME_BROWSER_ASH_CROSTINI_CROSTINI_FILE_SELECTOR_H_
diff --git a/chrome/browser/ash/dbus/dlp_files_policy_service_provider.cc b/chrome/browser/ash/dbus/dlp_files_policy_service_provider.cc
index 332c7dd..f54b2e9 100644
--- a/chrome/browser/ash/dbus/dlp_files_policy_service_provider.cc
+++ b/chrome/browser/ash/dbus/dlp_files_policy_service_provider.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ash/policy/dlp/dlp_files_controller.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/dbus/dlp/dlp_service.pb.h"
@@ -114,6 +115,7 @@
 
   dlp_files_controller_.IsFilesTransferRestricted(
       profile, source_urls, request.destination_url(),
+      policy::DlpWarnDialog::FilesAction::kTransfer,
       base::BindOnce(
           &DlpFilesPolicyServiceProvider::RespondWithRestrictedFilesTransfer,
           weak_ptr_factory_.GetWeakPtr(), method_call,
diff --git a/chrome/browser/ash/dbus/fusebox_service_provider.cc b/chrome/browser/ash/dbus/fusebox_service_provider.cc
index 3cd1e7a..54092081 100644
--- a/chrome/browser/ash/dbus/fusebox_service_provider.cc
+++ b/chrome/browser/ash/dbus/fusebox_service_provider.cc
@@ -121,11 +121,13 @@
       dbus::Response::FromMethodCall(method_call);
   dbus::MessageWriter writer(response.get());
 
-  int32_t mode_bits = info.is_directory ? S_IFDIR : S_IFREG;
-  mode_bits |= read_only ? 0550 : 0770;  // "r-xr-x---" versus "rwxrwx---".
-
   writer.AppendInt32(posix_error_code);
-  writer.AppendInt32(mode_bits);
+  // For historical reasons, the D-Bus protocol uses a *signed* int32_t, even
+  // though /usr/include/x86_64-linux-gnu/bits/typesizes.h says "#define
+  // __MODE_T_TYPE __U32_TYPE" (and hence MakeModeBits returns an *unsigned*
+  // uint32_t). We use a static_cast to convert between them.
+  writer.AppendInt32(static_cast<int32_t>(
+      fusebox::Server::MakeModeBits(info.is_directory, read_only)));
   writer.AppendInt64(info.size);
   writer.AppendDouble(info.last_accessed.ToDoubleT());
   writer.AppendDouble(info.last_modified.ToDoubleT());
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index 32722f12..2f56d3e5 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -2096,6 +2096,8 @@
         TestCase("breadcrumbsEliderMenuItemClick").FilesSwa(),
         TestCase("breadcrumbsEliderMenuItemTabLeft"),
         TestCase("breadcrumbsEliderMenuItemTabLeft").FilesSwa(),
+        TestCase("breadcrumbNavigateBackToSharedWithMe"),
+        TestCase("breadcrumbNavigateBackToSharedWithMe").FilesSwa(),
         TestCase("breadcrumbsEliderMenuItemTabRight"),
         TestCase("breadcrumbsEliderMenuItemTabRight").FilesSwa()));
 
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc
index 82fb82e..60392e9a 100644
--- a/chrome/browser/ash/file_manager/volume_manager.cc
+++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -41,7 +41,6 @@
 #include "chrome/browser/ash/crostini/crostini_manager.h"
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
-#include "chrome/browser/ash/drive/file_system_util.h"
 #include "chrome/browser/ash/file_manager/fusebox_mounter.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/file_manager/snapshot_manager.h"
@@ -69,6 +68,7 @@
 namespace file_manager {
 namespace {
 
+using l10n_util::GetStringUTF8;
 const uint32_t kAccessCapabilityReadWrite = 0;
 const uint32_t kFilesystemTypeGenericHierarchical = 2;
 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
@@ -248,21 +248,20 @@
 }
 
 // Returns the localized label for a given media view.
-std::string MediaViewDocumentIdToLabel(std::string root_document_id) {
-  if (root_document_id == arc::kAudioRootDocumentId) {
-    return l10n_util::GetStringUTF8(
-        IDS_FILE_BROWSER_MEDIA_VIEW_AUDIO_ROOT_LABEL);
-  } else if (root_document_id == arc::kImagesRootDocumentId) {
-    return l10n_util::GetStringUTF8(
-        IDS_FILE_BROWSER_MEDIA_VIEW_IMAGES_ROOT_LABEL);
-  } else if (root_document_id == arc::kVideosRootDocumentId) {
-    return l10n_util::GetStringUTF8(
-        IDS_FILE_BROWSER_MEDIA_VIEW_VIDEOS_ROOT_LABEL);
-  } else if (root_document_id == arc::kDocumentsRootDocumentId) {
-    return l10n_util::GetStringUTF8(
-        IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL);
-  }
-  NOTREACHED();
+std::string MediaViewDocumentIdToLabel(const base::StringPiece id) {
+  if (id == arc::kAudioRootDocumentId)
+    return GetStringUTF8(IDS_FILE_BROWSER_MEDIA_VIEW_AUDIO_ROOT_LABEL);
+
+  if (id == arc::kImagesRootDocumentId)
+    return GetStringUTF8(IDS_FILE_BROWSER_MEDIA_VIEW_IMAGES_ROOT_LABEL);
+
+  if (id == arc::kVideosRootDocumentId)
+    return GetStringUTF8(IDS_FILE_BROWSER_MEDIA_VIEW_VIDEOS_ROOT_LABEL);
+
+  if (id == arc::kDocumentsRootDocumentId)
+    return GetStringUTF8(IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL);
+
+  NOTREACHED() << "Unexpected root document ID: " << id;
   return "";
 }
 
@@ -272,30 +271,16 @@
   return out << VolumeTypeToString(type);
 }
 
-Volume::Volume()
-    : source_(SOURCE_FILE),
-      type_(VOLUME_TYPE_GOOGLE_DRIVE),
-      mount_condition_(ash::disks::MountCondition::kNone),
-      mount_context_(MOUNT_CONTEXT_UNKNOWN),
-      is_parent_(false),
-      is_read_only_(false),
-      is_read_only_removable_device_(false),
-      has_media_(false),
-      configurable_(false),
-      watchable_(false),
-      hidden_(false) {}
-
+Volume::Volume() = default;
 Volume::~Volume() = default;
 
 // static
-std::unique_ptr<Volume> Volume::CreateForDrive(
-    const base::FilePath& drive_path) {
+std::unique_ptr<Volume> Volume::CreateForDrive(base::FilePath drive_path) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_GOOGLE_DRIVE;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   volume->source_path_ = drive_path;
   volume->source_ = SOURCE_NETWORK;
-  volume->mount_path_ = drive_path;
+  volume->mount_path_ = std::move(drive_path);
   volume->volume_id_ = GenerateVolumeId(*volume);
   volume->volume_label_ =
       l10n_util::GetStringUTF8(IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL);
@@ -305,13 +290,12 @@
 
 // static
 std::unique_ptr<Volume> Volume::CreateForDownloads(
-    const base::FilePath& downloads_path) {
+    base::FilePath downloads_path) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_SYSTEM;
-  volume->mount_path_ = downloads_path;
+  volume->mount_path_ = std::move(downloads_path);
   volume->volume_id_ = GenerateVolumeId(*volume);
   volume->volume_label_ =
       l10n_util::GetStringUTF8(IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL);
@@ -343,7 +327,6 @@
     volume->drive_label_ = disk->drive_label();
   } else {
     volume->volume_label_ = volume->mount_path().BaseName().AsUTF8Unsafe();
-    volume->device_type_ = ash::DeviceType::kUnknown;
     volume->is_read_only_ =
         (mount_point.mount_type == ash::MountType::kArchive);
   }
@@ -390,7 +373,7 @@
 
 // static
 std::unique_ptr<Volume> Volume::CreateForFuseBoxProvidedFileSystem(
-    const base::FilePath& mount_path,
+    base::FilePath mount_path,
     const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
     MountContext mount_context) {
   std::unique_ptr<Volume> volume(new Volume());
@@ -413,7 +396,7 @@
 
   volume->type_ = VOLUME_TYPE_PROVIDED;
   volume->file_system_type_ = util::kFuseBox;
-  volume->mount_path_ = mount_path;
+  volume->mount_path_ = std::move(mount_path);
   volume->mount_context_ = mount_context;
 
   volume->is_parent_ = true;
@@ -434,17 +417,17 @@
 }
 
 // static
-std::unique_ptr<Volume> Volume::CreateForMTP(const base::FilePath& mount_path,
-                                             const std::string& label,
+std::unique_ptr<Volume> Volume::CreateForMTP(base::FilePath mount_path,
+                                             std::string label,
                                              bool read_only) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_MTP;
   volume->mount_path_ = mount_path;
   volume->is_parent_ = true;
   volume->is_read_only_ = read_only;
-  volume->volume_id_ = kMtpVolumeIdPrefix + label;
-  volume->volume_label_ = label;
-  volume->source_path_ = mount_path;
+  volume->volume_id_ = base::StrCat({kMtpVolumeIdPrefix, label});
+  volume->volume_label_ = std::move(label);
+  volume->source_path_ = std::move(mount_path);
   volume->source_ = SOURCE_DEVICE;
   volume->device_type_ = ash::DeviceType::kMobile;
 
@@ -456,23 +439,22 @@
 }
 
 // static
-std::unique_ptr<Volume> Volume::CreateForFuseBoxMTP(
-    const base::FilePath& mount_path,
-    const std::string& label,
-    bool read_only) {
+std::unique_ptr<Volume> Volume::CreateForFuseBoxMTP(base::FilePath mount_path,
+                                                    std::string label,
+                                                    bool read_only) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_MTP;
   volume->file_system_type_ = util::kFuseBox;
   volume->device_type_ = ash::DeviceType::kMobile;
   volume->source_path_ = mount_path;
   volume->source_ = SOURCE_DEVICE;
-  volume->mount_path_ = mount_path;
+  volume->mount_path_ = std::move(mount_path);
   volume->is_parent_ = true;
   volume->is_read_only_ = read_only;
   // "fusebox" prefix the original MTP volume id.
   volume->volume_id_ =
       base::StrCat({util::kFuseBox, kMtpVolumeIdPrefix, label});
-  volume->volume_label_ = label;
+  volume->volume_label_ = std::move(label);
   if (ash::features::IsFileManagerFuseBoxDebugEnabled())
     volume->volume_label_.insert(0, "fusebox ");
 
@@ -489,7 +471,6 @@
     const std::string& root_document_id) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_MEDIA_VIEW;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   volume->source_ = SOURCE_SYSTEM;
   volume->mount_path_ = arc::GetDocumentsProviderMountPath(
       arc::kMediaDocumentsProviderAuthority, root_document_id);
@@ -503,15 +484,14 @@
 
 // static
 std::unique_ptr<Volume> Volume::CreateForSshfsCrostini(
-    const base::FilePath& sshfs_mount_path,
-    const base::FilePath& remote_mount_path) {
+    base::FilePath sshfs_mount_path,
+    base::FilePath remote_mount_path) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_CROSTINI;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_SYSTEM;
-  volume->mount_path_ = sshfs_mount_path;
-  volume->remote_mount_path_ = remote_mount_path;
+  volume->mount_path_ = std::move(sshfs_mount_path);
+  volume->remote_mount_path_ = std::move(remote_mount_path);
   volume->volume_id_ = GenerateVolumeId(*volume);
   volume->volume_label_ =
       l10n_util::GetStringUTF8(IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL);
@@ -521,20 +501,19 @@
 
 // static
 std::unique_ptr<Volume> Volume::CreateForSftpGuestOs(
-    const std::string display_name,
-    const base::FilePath& sftp_mount_path,
-    const base::FilePath& remote_mount_path,
+    std::string display_name,
+    base::FilePath sftp_mount_path,
+    base::FilePath remote_mount_path,
     const guest_os::VmType vm_type) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = vm_type == guest_os::VmType::ARCVM ? VOLUME_TYPE_ANDROID_FILES
                                                      : VOLUME_TYPE_GUEST_OS;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_SYSTEM;
-  volume->mount_path_ = sftp_mount_path;
-  volume->remote_mount_path_ = remote_mount_path;
+  volume->mount_path_ = std::move(sftp_mount_path);
+  volume->remote_mount_path_ = std::move(remote_mount_path);
   volume->volume_id_ = GenerateVolumeId(*volume);
-  volume->volume_label_ = display_name;
+  volume->volume_label_ = std::move(display_name);
   volume->watchable_ = true;
   volume->vm_type_ = vm_type;
   return volume;
@@ -542,16 +521,15 @@
 
 // static
 std::unique_ptr<Volume> Volume::CreateForAndroidFiles(
-    const base::FilePath& mount_path) {
+    base::FilePath mount_path) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_ANDROID_FILES;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_SYSTEM;
-  volume->mount_path_ = mount_path;
+  volume->mount_path_ = std::move(mount_path);
   volume->volume_id_ = GenerateVolumeId(*volume);
   volume->volume_label_ =
-      l10n_util::GetStringUTF8(IDS_FILE_BROWSER_ANDROID_FILES_ROOT_LABEL);
+      GetStringUTF8(IDS_FILE_BROWSER_ANDROID_FILES_ROOT_LABEL);
   volume->watchable_ = true;
   return volume;
 }
@@ -567,7 +545,6 @@
     bool read_only) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_DOCUMENTS_PROVIDER;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_SYSTEM;
   volume->mount_path_ =
@@ -586,16 +563,15 @@
 }
 
 // static
-std::unique_ptr<Volume> Volume::CreateForSmb(const base::FilePath& mount_point,
-                                             const std::string display_name) {
+std::unique_ptr<Volume> Volume::CreateForSmb(base::FilePath mount_point,
+                                             std::string display_name) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_SMB;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_NETWORK;
-  volume->mount_path_ = mount_point;
+  volume->mount_path_ = std::move(mount_point);
   volume->volume_id_ = GenerateVolumeId(*volume);
-  volume->volume_label_ = display_name;
+  volume->volume_label_ = std::move(display_name);
   volume->watchable_ = false;
   volume->is_read_only_ = false;
   return volume;
@@ -606,14 +582,12 @@
 // through ImageLoader, which needs a Volume present to be able to read from the
 // directory.
 // static
-std::unique_ptr<Volume> Volume::CreateForShareCache(
-    const base::FilePath& mount_path) {
+std::unique_ptr<Volume> Volume::CreateForShareCache(base::FilePath mount_path) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = VOLUME_TYPE_SYSTEM_INTERNAL;
-  volume->device_type_ = ash::DeviceType::kUnknown;
   // Keep source_path empty.
   volume->source_ = SOURCE_SYSTEM;
-  volume->mount_path_ = mount_path;
+  volume->mount_path_ = std::move(mount_path);
   volume->volume_id_ = GenerateVolumeId(*volume);
   volume->watchable_ = false;
   volume->is_read_only_ = true;
@@ -622,57 +596,52 @@
 }
 
 // static
-std::unique_ptr<Volume> Volume::CreateForTesting(
-    const base::FilePath& path,
-    VolumeType volume_type,
-    ash::DeviceType device_type,
-    bool read_only,
-    const base::FilePath& device_path,
-    const std::string& drive_label,
-    const std::string& file_system_type,
-    bool hidden,
-    bool watchable) {
+std::unique_ptr<Volume> Volume::CreateForTesting(base::FilePath path,
+                                                 VolumeType volume_type,
+                                                 ash::DeviceType device_type,
+                                                 bool read_only,
+                                                 base::FilePath device_path,
+                                                 std::string drive_label,
+                                                 std::string file_system_type,
+                                                 bool hidden,
+                                                 bool watchable) {
   std::unique_ptr<Volume> volume(new Volume());
   volume->type_ = volume_type;
   volume->device_type_ = device_type;
   // Keep source_path empty.
   volume->source_ = SOURCE_DEVICE;
-  volume->mount_path_ = path;
-  volume->storage_device_path_ = device_path;
+  volume->mount_path_ = std::move(path);
+  volume->storage_device_path_ = std::move(device_path);
   volume->is_read_only_ = read_only;
   volume->volume_id_ = GenerateVolumeId(*volume);
-  volume->drive_label_ = drive_label;
-  if (volume_type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
-    volume->file_system_type_ = file_system_type;
-  }
+  volume->drive_label_ = std::move(drive_label);
+  volume->file_system_type_ = std::move(file_system_type);
   volume->hidden_ = hidden;
   volume->watchable_ = watchable;
   return volume;
 }
 
 // static
-std::unique_ptr<Volume> Volume::CreateForTesting(
-    const base::FilePath& device_path,
-    const base::FilePath& mount_path) {
+std::unique_ptr<Volume> Volume::CreateForTesting(base::FilePath device_path,
+                                                 base::FilePath mount_path) {
   std::unique_ptr<Volume> volume(new Volume());
-  volume->storage_device_path_ = device_path;
-  volume->mount_path_ = mount_path;
+  volume->storage_device_path_ = std::move(device_path);
+  volume->mount_path_ = std::move(mount_path);
   return volume;
 }
 
 // static
 std::unique_ptr<Volume> Volume::CreateForTesting(
-    const base::FilePath& path,
+    base::FilePath path,
     VolumeType volume_type,
     absl::optional<guest_os::VmType> vm_type,
-    absl::optional<base::FilePath> source_path) {
+    base::FilePath source_path) {
   std::unique_ptr<Volume> volume(new Volume());
-  volume->mount_path_ = path;
+  volume->mount_path_ = std::move(path);
   volume->type_ = volume_type;
   volume->vm_type_ = vm_type;
   volume->volume_id_ = GenerateVolumeId(*volume);
-  if (source_path.has_value())
-    volume->source_path_ = std::move(source_path.value());
+  volume->source_path_ = std::move(source_path);
   return volume;
 }
 
@@ -849,8 +818,8 @@
 
   std::vector<base::WeakPtr<Volume>> result;
   result.reserve(mounted_volumes_.size());
-  for (const auto& pair : mounted_volumes_) {
-    result.push_back(pair.second->AsWeakPtr());
+  for (const auto& volume : mounted_volumes_) {
+    result.push_back(volume->AsWeakPtr());
   }
   return result;
 }
@@ -861,17 +830,17 @@
 
   const auto it = mounted_volumes_.find(volume_id);
   if (it != mounted_volumes_.end())
-    return it->second->AsWeakPtr();
+    return it->get()->AsWeakPtr();
   return base::WeakPtr<Volume>();
 }
 
 base::WeakPtr<Volume> VolumeManager::FindVolumeFromPath(
     const base::FilePath& path) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  for (const auto& pair : mounted_volumes_) {
-    base::FilePath volume_mount_path = pair.second->mount_path();
+  for (const auto& volume : mounted_volumes_) {
+    base::FilePath volume_mount_path = volume->mount_path();
     if (path == volume_mount_path || volume_mount_path.IsParent(path)) {
-      return pair.second->AsWeakPtr();
+      return volume->AsWeakPtr();
     }
   }
   return nullptr;
@@ -884,8 +853,9 @@
   std::unique_ptr<Volume> volume =
       Volume::CreateForSshfsCrostini(sshfs_mount_path, remote_mount_path);
   // Ignore if volume already exists.
-  if (mounted_volumes_.find(volume->volume_id()) != mounted_volumes_.end())
+  if (mounted_volumes_.count(volume->volume_id()) != 0)
     return;
+
   DoMountEvent(ash::MountError::kNone, std::move(volume));
 
   // Listen for crostini container shutdown and remove volume.
@@ -909,7 +879,7 @@
   std::unique_ptr<Volume> volume = Volume::CreateForSftpGuestOs(
       display_name, sftp_mount_path, remote_mount_path, vm_type);
   // Ignore if volume already exists.
-  if (mounted_volumes_.find(volume->volume_id()) != mounted_volumes_.end())
+  if (mounted_volumes_.count(volume->volume_id()) != 0)
     return;
   DoMountEvent(ash::MountError::kNone, std::move(volume));
 }
@@ -1004,20 +974,19 @@
   return true;
 }
 
-void VolumeManager::AddVolumeForTesting(const base::FilePath& path,
+void VolumeManager::AddVolumeForTesting(base::FilePath path,
                                         VolumeType volume_type,
                                         ash::DeviceType device_type,
                                         bool read_only,
-                                        const base::FilePath& device_path,
-                                        const std::string& drive_label,
-                                        const std::string& file_system_type,
+                                        base::FilePath device_path,
+                                        std::string drive_label,
+                                        std::string file_system_type,
                                         bool hidden,
                                         bool watchable) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DoMountEvent(ash::MountError::kNone,
-               Volume::CreateForTesting(path, volume_type, device_type,
-                                        read_only, device_path, drive_label,
-                                        file_system_type, hidden, watchable));
+  AddVolumeForTesting(Volume::CreateForTesting(
+      std::move(path), volume_type, device_type, read_only,
+      std::move(device_path), std::move(drive_label),
+      std::move(file_system_type), hidden, watchable));
 }
 
 void VolumeManager::AddVolumeForTesting(std::unique_ptr<Volume> volume) {
@@ -1155,16 +1124,19 @@
   const ash::disks::Disk* const disk =
       disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
   std::unique_ptr<Volume> volume = Volume::CreateForRemovable(mount_info, disk);
+
   switch (event) {
     case ash::disks::DiskMountManager::MOUNTING: {
       DoMountEvent(error_code, std::move(volume));
       return;
     }
+
     case ash::disks::DiskMountManager::UNMOUNTING:
       DoUnmountEvent(error_code, *volume);
       return;
   }
-  NOTREACHED();
+
+  NOTREACHED() << "Unexpected event type " << event;
 }
 
 void VolumeManager::OnFormatEvent(
@@ -1173,8 +1145,8 @@
     const std::string& device_path,
     const std::string& device_label) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DVLOG(1) << "OnDeviceEvent: " << event << ", " << static_cast<int>(error_code)
-           << ", " << device_path;
+  DVLOG(1) << "OnFormatEvent: " << event << ", error = " << error_code
+           << ", device_path = " << device_path;
 
   switch (event) {
     case ash::disks::DiskMountManager::FORMAT_STARTED:
@@ -1183,6 +1155,7 @@
                                  error_code == ash::FormatError::kNone);
       }
       return;
+
     case ash::disks::DiskMountManager::FORMAT_COMPLETED:
       // Even if format did not complete successfully, try to mount the device
       // so the user can retry.
@@ -1201,7 +1174,8 @@
 
       return;
   }
-  NOTREACHED();
+
+  NOTREACHED() << "Unexpected FormatEvent " << event;
 }
 
 void VolumeManager::OnPartitionEvent(
@@ -1210,8 +1184,8 @@
     const std::string& device_path,
     const std::string& device_label) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DVLOG(1) << "OnPartitionEvent: " << event << ", "
-           << static_cast<int>(error_code) << ", " << device_path;
+  DVLOG(1) << "OnPartitionEvent: " << event << ", error = " << error_code
+           << ", device_path = " << device_path;
 
   switch (event) {
     case ash::disks::DiskMountManager::PARTITION_STARTED:
@@ -1220,6 +1194,7 @@
                                     error_code == ash::PartitionError::kNone);
       }
       return;
+
     case ash::disks::DiskMountManager::PARTITION_COMPLETED:
       // If partitioning failed, try to mount the device so the user can retry.
       // MountPath auto-detects filesystem format if second argument is
@@ -1238,7 +1213,8 @@
       }
       return;
   }
-  NOTREACHED();
+
+  NOTREACHED() << "Unexpected PartitionEvent " << event;
 }
 
 void VolumeManager::OnRenameEvent(
@@ -1247,8 +1223,8 @@
     const std::string& device_path,
     const std::string& device_label) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DVLOG(1) << "OnDeviceEvent: " << event << ", " << static_cast<int>(error_code)
-           << ", " << device_path;
+  DVLOG(1) << "OnRenameEvent: " << event << ", error = " << error_code
+           << ", device_path = " << device_path;
 
   switch (event) {
     case ash::disks::DiskMountManager::RENAME_STARTED:
@@ -1257,6 +1233,7 @@
                                  error_code == ash::RenameError::kNone);
       }
       return;
+
     case ash::disks::DiskMountManager::RENAME_COMPLETED:
       // Find previous mount point label if it exists
       std::string mount_label;
@@ -1283,7 +1260,8 @@
 
       return;
   }
-  NOTREACHED();
+
+  NOTREACHED() << "Unexpected RenameEvent " << event;
 }
 
 void VolumeManager::RestoreProvidedFileSystems() {
@@ -1586,7 +1564,7 @@
 
   // Assign a fresh volume ID based on the volume name.
   std::string label = base_name;
-  for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label); ++i)
+  for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label) != 0; ++i)
     label = base_name + base::StringPrintf(" (%d)", i);
 
   // Register the MTP storage device with chrome::storage.
@@ -1658,12 +1636,12 @@
     return;
 
   for (const auto& mounted_volume : mounted_volumes_) {
-    if (mounted_volume.second->source_path().value() != info.location())
+    if (mounted_volume->source_path().value() != info.location())
       continue;
 
     // Unmount the MTP storage device in files app.
-    const std::string volume_id = mounted_volume.second->volume_id();
-    DoUnmountEvent(ash::MountError::kNone, *mounted_volume.second.get());
+    const std::string volume_id = mounted_volume->volume_id();
+    DoUnmountEvent(ash::MountError::kNone, *mounted_volume);
 
     // Remove the MTP storage device from chrome::storage.
     const std::string fsid = GetMountPointNameForMediaStorage(info);
@@ -1810,17 +1788,20 @@
 }
 
 void VolumeManager::DoMountEvent(ash::MountError error_code,
-                                 std::unique_ptr<Volume> volume) {
+                                 std::unique_ptr<Volume> volume_ptr) {
+  DCHECK(volume_ptr);
+  const Volume& volume = *volume_ptr;
+
   // Archive files are mounted globally in system. We however don't want to show
   // archives from profile-specific folders (Drive/Downloads) of other users in
   // multi-profile session. To this end, we filter out archives not on the
   // volumes already mounted on this VolumeManager instance.
-  if (volume->type() == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
+  if (volume.type() == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
     // Source may be in Drive cache folder under the current profile directory.
     bool from_current_profile =
-        profile_->GetPath().IsParent(volume->source_path());
+        profile_->GetPath().IsParent(volume.source_path());
     for (const auto& mounted_volume : mounted_volumes_) {
-      if (mounted_volume.second->mount_path().IsParent(volume->source_path())) {
+      if (mounted_volume->mount_path().IsParent(volume.source_path())) {
         from_current_profile = true;
         break;
       }
@@ -1830,36 +1811,71 @@
   }
 
   // Filter out removable disks if forbidden by policy for this profile.
-  if (volume->type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
+  if (volume.type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
       profile_->GetPrefs()->GetBoolean(
           disks::prefs::kExternalStorageDisabled)) {
     return;
   }
 
-  Volume* raw_volume = volume.get();
   if (error_code == ash::MountError::kNone ||
-      volume->mount_condition() != ash::disks::MountCondition::kNone) {
-    mounted_volumes_[volume->volume_id()] = std::move(volume);
-    UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", raw_volume->type(),
+      volume.mount_condition() != ash::disks::MountCondition::kNone) {
+    const auto [it, ok] = mounted_volumes_.insert(std::move(volume_ptr));
+    if (!ok) {
+      LOG(WARNING) << "crbug.com/1354029: Volume '" << volume.volume_id()
+                   << "' is already registered";
+      DCHECK(volume_ptr);
+      DCHECK_EQ((*it)->volume_id(), volume.volume_id());
+
+      // TODO(crbug.com/1354029) Is it possible for a Volume object with
+      // different properties to be inserted here?
+      DCHECK_EQ((*it)->configurable(), volume.configurable());
+      DCHECK_EQ((*it)->device_type(), volume.device_type());
+      DCHECK_EQ((*it)->drive_label(), volume.drive_label());
+      DCHECK_EQ((*it)->file_system_id(), volume.file_system_id());
+      DCHECK_EQ((*it)->file_system_type(), volume.file_system_type());
+      DCHECK_EQ((*it)->has_media(), volume.has_media());
+      DCHECK_EQ((*it)->hidden(), volume.hidden());
+      DCHECK_EQ((*it)->is_parent(), volume.is_parent());
+      DCHECK_EQ((*it)->is_read_only_removable_device(),
+                volume.is_read_only_removable_device());
+      DCHECK_EQ((*it)->is_read_only(), volume.is_read_only());
+      DCHECK_EQ((*it)->mount_condition(), volume.mount_condition());
+      DCHECK_EQ((*it)->mount_context(), volume.mount_context());
+      DCHECK_EQ((*it)->mount_path(), volume.mount_path());
+      DCHECK_EQ((*it)->remote_mount_path(), volume.remote_mount_path());
+      DCHECK_EQ((*it)->source_path(), volume.source_path());
+      DCHECK_EQ((*it)->source(), volume.source());
+      DCHECK_EQ((*it)->storage_device_path(), volume.storage_device_path());
+      DCHECK_EQ((*it)->type(), volume.type());
+      DCHECK_EQ((*it)->volume_label(), volume.volume_label());
+      DCHECK_EQ((*it)->watchable(), volume.watchable());
+
+      // Replace the Volume in |mounted_volumes_|.
+      const_cast<std::unique_ptr<Volume>&>(*it) = std::move(volume_ptr);
+    }
+
+    DCHECK_EQ(&volume, it->get());
+    UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", volume.type(),
                               NUM_VOLUME_TYPE);
   }
 
   for (auto& observer : observers_)
-    observer.OnVolumeMounted(error_code, *raw_volume);
+    observer.OnVolumeMounted(error_code, volume);
 }
 
 void VolumeManager::DoUnmountEvent(ash::MountError error_code,
                                    const Volume& volume) {
-  auto iter = mounted_volumes_.find(volume.volume_id());
-  if (iter == mounted_volumes_.end())
+  const Volumes::const_iterator it = mounted_volumes_.find(volume.volume_id());
+  if (it == mounted_volumes_.end())
     return;
-  std::unique_ptr<Volume> volume_ref;
-  if (error_code == ash::MountError::kNone) {
-    // It is important to hold a reference to the removed Volume from
-    // |mounted_volumes_|, because OnVolumeMounted() will access it.
-    volume_ref = std::move(iter->second);
-    mounted_volumes_.erase(iter);
-  }
+
+  DCHECK_EQ(volume.volume_id(), it->get()->volume_id());
+
+  // Hold a reference to the removed Volume from |mounted_volumes_|, because
+  // OnVolumeMounted() will access it.
+  Volumes::node_type node_to_delete;
+  if (error_code == ash::MountError::kNone)
+    node_to_delete = mounted_volumes_.extract(it);
 
   for (auto& observer : observers_)
     observer.OnVolumeUnmounted(error_code, volume);
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h
index 8c11cff..4e4c3a7 100644
--- a/chrome/browser/ash/file_manager/volume_manager.h
+++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_BROWSER_ASH_FILE_MANAGER_VOLUME_MANAGER_H_
 #define CHROME_BROWSER_ASH_FILE_MANAGER_VOLUME_MANAGER_H_
 
-#include <map>
 #include <memory>
 #include <optional>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -102,42 +102,52 @@
   ~Volume();
 
   // Factory static methods for different volume types.
-  static std::unique_ptr<Volume> CreateForDrive(
-      const base::FilePath& drive_path);
+  static std::unique_ptr<Volume> CreateForDrive(base::FilePath drive_path);
+
   static std::unique_ptr<Volume> CreateForDownloads(
-      const base::FilePath& downloads_path);
+      base::FilePath downloads_path);
+
   static std::unique_ptr<Volume> CreateForRemovable(
       const ash::disks::DiskMountManager::MountPoint& mount_point,
       const ash::disks::Disk* disk);
+
   static std::unique_ptr<Volume> CreateForProvidedFileSystem(
       const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
       MountContext mount_context);
+
   static std::unique_ptr<Volume> CreateForFuseBoxProvidedFileSystem(
-      const base::FilePath& mount_path,
+      base::FilePath mount_path,
       const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
       MountContext mount_context);
-  static std::unique_ptr<Volume> CreateForMTP(const base::FilePath& mount_path,
-                                              const std::string& label,
+
+  static std::unique_ptr<Volume> CreateForMTP(base::FilePath mount_path,
+                                              std::string label,
                                               bool read_only);
-  static std::unique_ptr<Volume> CreateForFuseBoxMTP(
-      const base::FilePath& mount_path,
-      const std::string& label,
-      bool read_only);
+
+  static std::unique_ptr<Volume> CreateForFuseBoxMTP(base::FilePath mount_path,
+                                                     std::string label,
+                                                     bool read_only);
+
   static std::unique_ptr<Volume> CreateForMediaView(
       const std::string& root_document_id);
+
   static std::unique_ptr<Volume> CreateMediaViewForTesting(
       base::FilePath mount_path,
       const std::string& root_document_id);
+
   static std::unique_ptr<Volume> CreateForSshfsCrostini(
-      const base::FilePath& crostini_path,
-      const base::FilePath& remote_mount_path);
+      base::FilePath crostini_path,
+      base::FilePath remote_mount_path);
+
   static std::unique_ptr<Volume> CreateForSftpGuestOs(
-      const std::string display_name,
-      const base::FilePath& sftp_mount_path,
-      const base::FilePath& remote_mount_path,
+      std::string display_name,
+      base::FilePath sftp_mount_path,
+      base::FilePath remote_mount_path,
       const guest_os::VmType vm_type);
+
   static std::unique_ptr<Volume> CreateForAndroidFiles(
-      const base::FilePath& mount_path);
+      base::FilePath mount_path);
+
   static std::unique_ptr<Volume> CreateForDocumentsProvider(
       const std::string& authority,
       const std::string& root_id,
@@ -146,33 +156,36 @@
       const std::string& summary,
       const GURL& icon_url,
       bool read_only);
-  static std::unique_ptr<Volume> CreateForSmb(const base::FilePath& mount_point,
-                                              const std::string display_name);
-  static std::unique_ptr<Volume> CreateForShareCache(
-      const base::FilePath& mount_path);
+
+  static std::unique_ptr<Volume> CreateForSmb(base::FilePath mount_point,
+                                              std::string display_name);
+
+  static std::unique_ptr<Volume> CreateForShareCache(base::FilePath mount_path);
+
   static std::unique_ptr<Volume> CreateForTesting(
-      const base::FilePath& path,
+      base::FilePath path,
       VolumeType volume_type,
       ash::DeviceType device_type,
       bool read_only,
-      const base::FilePath& device_path,
-      const std::string& drive_label,
-      const std::string& file_system_type = "",
+      base::FilePath device_path,
+      std::string drive_label,
+      std::string file_system_type = {},
       bool hidden = false,
       bool watchable = false);
-  static std::unique_ptr<Volume> CreateForTesting(
-      const base::FilePath& device_path,
-      const base::FilePath& mount_path);
+
+  static std::unique_ptr<Volume> CreateForTesting(base::FilePath device_path,
+                                                  base::FilePath mount_path);
+
   // Create a volume at `path` with the specified `volume_type`.
   // For `volume_type`==VOLUME_TYPE_GUEST_OS, `vm_type` should also be
   // specified. For `volume_type`==VOLUME_TYPE_MOUNTED_ARCHIVE_FILE,
   // `source_path` has to be specified and point to the (not necessarily
   // existing) path of the archive file.
   static std::unique_ptr<Volume> CreateForTesting(
-      const base::FilePath& path,
+      base::FilePath path,
       VolumeType volume_type,
       absl::optional<guest_os::VmType> vm_type,
-      absl::optional<base::FilePath> source_path = absl::nullopt);
+      base::FilePath source_path = {});
 
   // Getters for all members. See below for details.
   const std::string& volume_id() const { return volume_id_; }
@@ -233,10 +246,10 @@
   ash::file_system_provider::ProviderId provider_id_;
 
   // The source of the volume's data.
-  Source source_;
+  Source source_ = SOURCE_FILE;
 
   // The type of mounted volume.
-  VolumeType type_;
+  VolumeType type_ = VOLUME_TYPE_GOOGLE_DRIVE;
 
   // The type of device. (e.g. USB, SD card, DVD etc.)
   ash::DeviceType device_type_ = ash::DeviceType::kUnknown;
@@ -258,36 +271,47 @@
   base::FilePath remote_mount_path_;
 
   // The mounting condition. See the enum for the details.
-  ash::disks::MountCondition mount_condition_;
+  ash::disks::MountCondition mount_condition_ =
+      ash::disks::MountCondition::kNone;
 
   // The context of the mount. Whether mounting was performed due to a user
   // interaction or not.
-  MountContext mount_context_;
+  MountContext mount_context_ = MOUNT_CONTEXT_UNKNOWN;
 
   // Path of the storage device this device's block is a part of.
   // (e.g. /sys/devices/pci0000:00/.../8:0:0:0/)
   base::FilePath storage_device_path_;
 
-  // Label for the volume if the volume is either removable or a provided
-  // file system. In case of removables, if disk is a parent, then its label,
-  // else parents label (e.g. "TransMemory").
+  // Label for the volume if the volume is either removable or a provided file
+  // system. In case of removables, if disk is a parent, then its label, else
+  // parents label (e.g. "TransMemory").
   std::string volume_label_;
 
+  // Identifier for the file system type
+  std::string file_system_type_;
+
+  // Volume icon set.
+  ash::file_system_provider::IconSet icon_set_;
+
+  // Device label of a physical removable device. Removable partitions belonging
+  // to the same device share the same device label.
+  std::string drive_label_;
+
   // Is the device is a parent device (i.e. sdb rather than sdb1).
-  bool is_parent_;
+  bool is_parent_ = false;
 
   // True if the volume is not writable by applications.
-  bool is_read_only_;
+  bool is_read_only_ = false;
 
   // True if the volume is made read_only due to its hardware.
   // This implies is_read_only_.
-  bool is_read_only_removable_device_;
+  bool is_read_only_removable_device_ = false;
 
   // True if the volume contains media.
-  bool has_media_;
+  bool has_media_ = false;
 
   // True if the volume is configurable.
-  bool configurable_;
+  bool configurable_ = false;
 
   // True if the volume notifies about changes via file/directory watchers.
   //
@@ -296,19 +320,9 @@
   // so that the directory contents can be manually refreshed.
   bool watchable_ = false;
 
-  // Identifier for the file system type
-  std::string file_system_type_;
-
-  // Volume icon set.
-  ash::file_system_provider::IconSet icon_set_;
-
-  // Device label of a physical removable device. Removable partitions
-  // belonging to the same device share the same device label.
-  std::string drive_label_;
-
   // True if the volume is hidden and never shown to the user through File
   // Manager.
-  bool hidden_;
+  bool hidden_ = false;
 
   // Only set for VOLUME_TYPE_GUEST_OS, identifies the type of Guest OS VM.
   absl::optional<guest_os::VmType> vm_type_;
@@ -434,13 +448,13 @@
 
   // For testing purposes, adds a volume info pointing to |path|, with TESTING
   // type. Assumes that the mount point is already registered.
-  void AddVolumeForTesting(const base::FilePath& path,
+  void AddVolumeForTesting(base::FilePath path,
                            VolumeType volume_type,
                            ash::DeviceType device_type,
                            bool read_only,
-                           const base::FilePath& device_path = base::FilePath(),
-                           const std::string& drive_label = "",
-                           const std::string& file_system_type = "",
+                           base::FilePath device_path = {},
+                           std::string drive_label = {},
+                           std::string file_system_type = {},
                            bool hidden = false,
                            bool watchable = false);
 
@@ -546,6 +560,26 @@
   }
 
  private:
+  // Comparator sorting Volume objects by volume ID .
+  struct SortByVolumeId {
+    using is_transparent = void;
+
+    template <typename A, typename B>
+    bool operator()(const A& a, const B& b) const {
+      return GetKey(a) < GetKey(b);
+    }
+
+    static base::StringPiece GetKey(const base::StringPiece a) { return a; }
+
+    static base::StringPiece GetKey(const std::unique_ptr<Volume>& volume) {
+      DCHECK(volume);
+      return volume->volume_id();
+    }
+  };
+
+  // Set of Volume objects indexed by volume ID.
+  using Volumes = std::set<std::unique_ptr<Volume>, SortByVolumeId>;
+
   void RestoreProvidedFileSystems();
   void OnDiskMountManagerRefreshed(bool success);
   void OnStorageMonitorInitialized();
@@ -578,7 +612,7 @@
   ash::file_system_provider::Service*
       file_system_provider_service_;  // Not owned by this class.
   GetMtpStorageInfoCallback get_mtp_storage_info_callback_;
-  std::map<std::string, std::unique_ptr<Volume>> mounted_volumes_;
+  Volumes mounted_volumes_;
   std::unique_ptr<FuseBoxMounter> fusebox_mounter_;
   std::unique_ptr<SnapshotManager> snapshot_manager_;
   std::unique_ptr<DocumentsProviderRootManager>
diff --git a/chrome/browser/ash/fusebox/fusebox_server.cc b/chrome/browser/ash/fusebox/fusebox_server.cc
index 1ba3bf8..b8ff5c5 100644
--- a/chrome/browser/ash/fusebox/fusebox_server.cc
+++ b/chrome/browser/ash/fusebox/fusebox_server.cc
@@ -223,6 +223,7 @@
 void RunReadDirCallback(
     Server::ReadDirCallback callback,
     scoped_refptr<storage::FileSystemContext> fs_context,  // See § above.
+    bool read_only,
     uint64_t cookie,
     base::File::Error error_code,
     storage::AsyncFileUtil::EntryList entry_list,
@@ -231,10 +232,11 @@
 
   fusebox::DirEntryListProto protos;
   for (const auto& entry : entry_list) {
+    bool is_directory = entry.type == filesystem::mojom::FsFileType::DIRECTORY;
     auto* proto = protos.add_entries();
-    proto->set_is_directory(entry.type ==
-                            filesystem::mojom::FsFileType::DIRECTORY);
+    proto->set_is_directory(is_directory);
     proto->set_name(entry.name.value());
+    proto->set_mode_bits(Server::MakeModeBits(is_directory, read_only));
   }
 
   callback.Run(cookie, FileErrorToErrno(error_code), std::move(protos),
@@ -265,6 +267,13 @@
   return g_server_instance;
 }
 
+// static
+uint32_t Server::MakeModeBits(bool is_directory, bool read_only) {
+  uint32_t mode_bits = is_directory ? S_IFDIR : S_IFREG;
+  mode_bits |= read_only ? 0550 : 0770;  // "r-xr-x---" versus "rwxrwx---".
+  return mode_bits;
+}
+
 Server::Server(Delegate* delegate) : delegate_(delegate) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!g_server_instance);
@@ -378,10 +387,10 @@
     return;
   }
 
-  auto outer_callback =
-      base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
-                         base::BindRepeating(&RunReadDirCallback, callback,
-                                             common.fs_context, cookie));
+  auto outer_callback = base::BindPostTask(
+      base::SequencedTaskRunnerHandle::Get(),
+      base::BindRepeating(&RunReadDirCallback, callback, common.fs_context,
+                          common.read_only, cookie));
 
   content::GetIOThreadTaskRunner({})->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/ash/fusebox/fusebox_server.h b/chrome/browser/ash/fusebox/fusebox_server.h
index cde3629..3a11c03b 100644
--- a/chrome/browser/ash/fusebox/fusebox_server.h
+++ b/chrome/browser/ash/fusebox/fusebox_server.h
@@ -27,6 +27,9 @@
   // Returns a pointer to the global Server instance.
   static Server* GetInstance();
 
+  // Returns POSIX style (S_IFREG | rwxr-x---) bits.
+  static uint32_t MakeModeBits(bool is_directory, bool read_only);
+
   // The delegate should live longer than the server.
   explicit Server(Delegate* delegate);
   Server(const Server&) = delete;
diff --git a/chrome/browser/ash/game_mode/game_mode_controller.cc b/chrome/browser/ash/game_mode/game_mode_controller.cc
index 2e2d2d4..543960e 100644
--- a/chrome/browser/ash/game_mode/game_mode_controller.cc
+++ b/chrome/browser/ash/game_mode/game_mode_controller.cc
@@ -78,7 +78,8 @@
 void GameModeController::WindowTracker::UpdateGameModeStatus(
     ash::WindowState* window_state) {
   if (!game_mode_ && window_state->IsFullscreen()) {
-    game_mode_ = std::make_unique<GameModeEnabler>();
+    game_mode_ = std::make_unique<GameModeEnabler>(
+        ash::ResourcedClient::GameMode::BOREALIS);
   } else if (game_mode_ && !window_state->IsFullscreen()) {
     game_mode_.reset();
   }
@@ -93,13 +94,18 @@
 
 bool GameModeController::GameModeEnabler::should_record_failure;
 
-GameModeController::GameModeEnabler::GameModeEnabler() {
+GameModeController::GameModeEnabler::GameModeEnabler(
+    ash::ResourcedClient::GameMode mode)
+    : mode_(mode) {
+  DCHECK(mode != ash::ResourcedClient::GameMode::OFF);
+
   GameModeEnabler::should_record_failure = true;
   RecordBorealisGameModeResultHistogram(BorealisGameModeResult::kAttempted);
   if (ash::ResourcedClient::Get()) {
     ash::ResourcedClient::Get()->SetGameModeWithTimeout(
-        ash::ResourcedClient::GameMode::BOREALIS, kTimeoutSec,
-        base::BindOnce(&GameModeEnabler::OnSetGameMode, false));
+        mode_, kTimeoutSec,
+        base::BindOnce(&GameModeEnabler::OnSetGameMode,
+                       /*refresh_of=*/absl::nullopt));
   }
   timer_.Start(FROM_HERE, base::Seconds(kRefreshSec), this,
                &GameModeEnabler::RefreshGameMode);
@@ -110,26 +116,26 @@
   if (ash::ResourcedClient::Get()) {
     ash::ResourcedClient::Get()->SetGameModeWithTimeout(
         ash::ResourcedClient::GameMode::OFF, 0,
-        base::BindOnce(&GameModeEnabler::OnSetGameMode, true));
+        base::BindOnce(&GameModeEnabler::OnSetGameMode, /*refresh_of=*/mode_));
   }
 }
 
 void GameModeController::GameModeEnabler::RefreshGameMode() {
   if (ash::ResourcedClient::Get()) {
     ash::ResourcedClient::Get()->SetGameModeWithTimeout(
-        ash::ResourcedClient::GameMode::BOREALIS, kTimeoutSec,
-        base::BindOnce(&GameModeEnabler::OnSetGameMode, true));
+        mode_, kTimeoutSec,
+        base::BindOnce(&GameModeEnabler::OnSetGameMode, /*refresh_of=*/mode_));
   }
 }
 
 // Previous is whether game mode was enabled previous to this call.
 void GameModeController::GameModeEnabler::OnSetGameMode(
-    bool was_refresh,
+    absl::optional<ash::ResourcedClient::GameMode> refresh_of,
     absl::optional<ash::ResourcedClient::GameMode> previous) {
   if (!previous.has_value()) {
     LOG(ERROR) << "Failed to set Game Mode";
-  } else if (GameModeEnabler::should_record_failure && was_refresh &&
-             previous.value() != ash::ResourcedClient::GameMode::BOREALIS) {
+  } else if (GameModeEnabler::should_record_failure && refresh_of.has_value() &&
+             previous.value() != refresh_of.value()) {
     // If game mode was not on and it was not the initial call,
     // it means the previous call failed/timed out.
     RecordBorealisGameModeResultHistogram(BorealisGameModeResult::kFailed);
diff --git a/chrome/browser/ash/game_mode/game_mode_controller.h b/chrome/browser/ash/game_mode/game_mode_controller.h
index daf2af35..7f474d2 100644
--- a/chrome/browser/ash/game_mode/game_mode_controller.h
+++ b/chrome/browser/ash/game_mode/game_mode_controller.h
@@ -43,18 +43,19 @@
 
   class GameModeEnabler {
    public:
-    GameModeEnabler();
+    explicit GameModeEnabler(ash::ResourcedClient::GameMode mode);
     ~GameModeEnabler();
 
    private:
     static void OnSetGameMode(
-        bool was_refresh,
+        absl::optional<ash::ResourcedClient::GameMode> refresh_of,
         absl::optional<ash::ResourcedClient::GameMode> previous);
     void RefreshGameMode();
 
     // Used to determine if it's the first instance of game mode failing.
     static bool should_record_failure;
     base::RepeatingTimer timer_;
+    const ash::ResourcedClient::GameMode mode_;
   };
 
   class WindowTracker : public ash::WindowStateObserver,
diff --git a/chrome/browser/ash/login/session/user_session_initializer.cc b/chrome/browser/ash/login/session/user_session_initializer.cc
index 8dda7a1..8ad004a 100644
--- a/chrome/browser/ash/login/session/user_session_initializer.cc
+++ b/chrome/browser/ash/login/session/user_session_initializer.cc
@@ -16,7 +16,6 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/ash/arc/session/arc_service_launcher.h"
-#include "chrome/browser/ash/bruschetta/bruschetta_service.h"
 #include "chrome/browser/ash/camera_mic/vm_camera_mic_manager.h"
 #include "chrome/browser/ash/child_accounts/child_status_reporting_service_factory.h"
 #include "chrome/browser/ash/child_accounts/child_user_service_factory.h"
@@ -232,10 +231,6 @@
       crostini::CrostiniManager::GetForProfile(profile);
   if (crostini_manager)
     crostini_manager->MaybeUpdateCrostini();
-  if (base::FeatureList::IsEnabled(features::kBruschetta)) {
-    // Ensure the Bruschetta Service is running.
-    bruschetta::BruschettaService::GetForProfile(profile);
-  }
 
   clipboard_image_model_factory_impl_ =
       std::make_unique<ClipboardImageModelFactoryImpl>(profile);
diff --git a/chrome/browser/ash/policy/dlp/dlp_files_controller.cc b/chrome/browser/ash/policy/dlp/dlp_files_controller.cc
index dc40589..897e87790 100644
--- a/chrome/browser/ash/policy/dlp/dlp_files_controller.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_files_controller.cc
@@ -6,6 +6,7 @@
 
 #include <sys/types.h>
 #include <iterator>
+#include <memory>
 #include <string>
 
 #include "base/bind.h"
@@ -20,6 +21,8 @@
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h"
 #include "chromeos/dbus/dlp/dlp_client.h"
 #include "chromeos/dbus/dlp/dlp_service.pb.h"
 #include "storage/browser/file_system/file_system_url.h"
@@ -96,7 +99,8 @@
 DlpFilesController::DlpFileRestrictionDetails::~DlpFileRestrictionDetails() =
     default;
 
-DlpFilesController::DlpFilesController() = default;
+DlpFilesController::DlpFilesController()
+    : warn_notifier_(std::make_unique<DlpWarnNotifier>()) {}
 
 DlpFilesController::~DlpFilesController() = default;
 
@@ -192,6 +196,7 @@
     Profile* profile,
     std::vector<GURL> files_sources,
     std::string destination,
+    DlpWarnDialog::FilesAction files_action,
     IsFilesTransferRestrictedCallback result_callback) {
   DCHECK(profile);
   policy::DlpRulesManager* dlp_rules_manager =
@@ -204,6 +209,7 @@
   auto dst_component =
       MapFilePathtoPolicyComponent(profile, base::FilePath(destination));
   std::vector<GURL> restricted_files_sources;
+  std::vector<GURL> warned_files_sources;
   for (const auto& src : files_sources) {
     DlpRulesManager::Level level;
     if (dst_component.has_value()) {
@@ -220,8 +226,34 @@
 
     if (level == DlpRulesManager::Level::kBlock)
       restricted_files_sources.push_back(src);
+    else if (level == DlpRulesManager::Level::kWarn)
+      warned_files_sources.push_back(src);
   }
-  std::move(result_callback).Run(std::move(restricted_files_sources));
+
+  if (warned_files_sources.empty()) {
+    std::move(result_callback).Run(std::move(restricted_files_sources));
+    return;
+  }
+
+  warn_notifier_->ShowDlpFilesWarningDialog(
+      base::BindOnce(
+          &DlpFilesController::OnDlpWarnDialogReply,
+          weak_ptr_factory_.GetWeakPtr(), std::move(restricted_files_sources),
+          std::move(warned_files_sources), std::move(result_callback)),
+      files_action);
+}
+
+void DlpFilesController::OnDlpWarnDialogReply(
+    std::vector<GURL> restricted_files_sources,
+    std::vector<GURL> warned_files_sources,
+    IsFilesTransferRestrictedCallback callback,
+    bool should_proceed) {
+  std::vector<GURL> blocked_files_sources(restricted_files_sources);
+  if (!should_proceed)
+    blocked_files_sources.insert(blocked_files_sources.end(),
+                                 warned_files_sources.begin(),
+                                 warned_files_sources.end());
+  std::move(callback).Run(std::move(blocked_files_sources));
 }
 
 std::vector<DlpFilesController::DlpFileRestrictionDetails>
@@ -295,6 +327,12 @@
   return restricted;
 }
 
+void DlpFilesController::SetWarnNotifierForTesting(
+    std::unique_ptr<DlpWarnNotifier> warn_notifier) {
+  DCHECK(warn_notifier);
+  warn_notifier_ = std::move(warn_notifier);
+}
+
 void DlpFilesController::ReturnDisallowedTransfers(
     base::flat_map<std::string, storage::FileSystemURL> files_map,
     GetDisallowedTransfersCallback result_callback,
diff --git a/chrome/browser/ash/policy/dlp/dlp_files_controller.h b/chrome/browser/ash/policy/dlp/dlp_files_controller.h
index 6ccbb72..55d15fa 100644
--- a/chrome/browser/ash/policy/dlp/dlp_files_controller.h
+++ b/chrome/browser/ash/policy/dlp/dlp_files_controller.h
@@ -11,6 +11,8 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h"
 #include "chromeos/dbus/dlp/dlp_service.pb.h"
 #include "storage/browser/file_system/file_system_url.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom-forward.h"
@@ -102,12 +104,13 @@
       const GURL& destination,
       FilterDisallowedUploadsCallback result_callback);
 
-  // Returns a list of |files_sources| from which files aren't allowed to
-  // be transferred to |destination| in |result_callback|.
+  // Returns a sublist of |files_sources| in |result_callback| with files
+  // sources restricted from performing |action| to |destination|.
   void IsFilesTransferRestricted(
       Profile* profile,
       std::vector<GURL> files_sources,
       std::string destination,
+      DlpWarnDialog::FilesAction files_action,
       IsFilesTransferRestrictedCallback result_callback);
 
   // Returns restriction information for `sourceUrl`.
@@ -117,7 +120,18 @@
   // Returns whether a dlp policy matches for the `source_url`.
   bool IsDlpPolicyMatched(const std::string& source_url);
 
+  void SetWarnNotifierForTesting(
+      std::unique_ptr<DlpWarnNotifier> warn_notifier);
+
  private:
+  // Called back from warning dialog. Passes blocked files sources along
+  // to |callback|. In case |should_proceed| is true, passes only
+  // |restricted_files_sources|, otherwise passes also |warned_files_sources|.
+  void OnDlpWarnDialogReply(std::vector<GURL> restricted_files_sources,
+                            std::vector<GURL> warned_files_sources,
+                            IsFilesTransferRestrictedCallback callback,
+                            bool should_proceed);
+
   void ReturnDisallowedTransfers(
       base::flat_map<std::string, storage::FileSystemURL> files_map,
       GetDisallowedTransfersCallback result_callback,
@@ -131,6 +145,9 @@
                          GetDlpMetadataCallback result_callback,
                          const dlp::GetFilesSourcesResponse response);
 
+  // Is used for creating and showing the warning dialog.
+  std::unique_ptr<DlpWarnNotifier> warn_notifier_;
+
   base::WeakPtrFactory<DlpFilesController> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc b/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc
index 3d9dd3e..14031e01 100644
--- a/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_files_controller_unittest.cc
@@ -25,7 +25,9 @@
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h"
 #include "chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/mock_dlp_warn_notifier.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/dbus/chunneld/chunneld_client.h"
@@ -581,8 +583,68 @@
       blink::StorageKey(), mount_name, base::FilePath(path));
   ASSERT_TRUE(dst_url.is_valid());
 
-  files_controller_.IsFilesTransferRestricted(profile_.get(), files_sources,
-                                              dst_url.path().value(), cb.Get());
+  files_controller_.IsFilesTransferRestricted(
+      profile_.get(), files_sources, dst_url.path().value(),
+      DlpWarnDialog::FilesAction::kDownload, cb.Get());
+}
+
+class DlpFilesWarningDialogTest
+    : public DlpFilesControllerTest,
+      public ::testing::WithParamInterface<
+          std::tuple<bool, std::vector<std::string>>> {};
+
+INSTANTIATE_TEST_SUITE_P(
+    DlpFiles,
+    DlpFilesWarningDialogTest,
+    ::testing::Values(
+        std::make_tuple(true, std::vector<std::string>({kExample1})),
+        std::make_tuple(false,
+                        std::vector<std::string>({kExample1, kExample2}))));
+
+TEST_P(DlpFilesWarningDialogTest,
+       IsFilesTransferRestricted_FileDownloadWarned) {
+  auto [should_proceed, disallowed_sources_str] = GetParam();
+  std::vector<GURL> disallowed_sources;
+  for (const auto& source : disallowed_sources_str)
+    disallowed_sources.emplace_back(source);
+  std::vector<GURL> files_sources(
+      {GURL(kExample1), GURL(kExample2), GURL(kExample3)});
+
+  storage::ExternalMountPoints* mount_points =
+      storage::ExternalMountPoints::GetSystemInstance();
+  ASSERT_TRUE(mount_points);
+  mount_points->RevokeAllFileSystems();
+  ASSERT_TRUE(mount_points->RegisterFileSystem(
+      chromeos::kSystemMountNameRemovable, storage::kFileSystemTypeLocal,
+      storage::FileSystemMountOption(),
+      base::FilePath(file_manager::util::kRemovableMediaPath)));
+
+  std::unique_ptr<MockDlpWarnNotifier> wrapper =
+      std::make_unique<MockDlpWarnNotifier>(should_proceed);
+  MockDlpWarnNotifier* mock_dlp_warn_notifier = wrapper.get();
+  files_controller_.SetWarnNotifierForTesting(std::move(wrapper));
+
+  EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog).Times(1);
+
+  MockIsFilesTransferRestrictedCallback cb;
+  EXPECT_CALL(cb, Run(disallowed_sources)).Times(1);
+
+  EXPECT_CALL(*rules_manager_,
+              IsRestrictedComponent(_, DlpRulesManager::Component::kUsb, _, _))
+      .WillOnce(testing::Return(DlpRulesManager::Level::kBlock))
+      .WillOnce(testing::Return(DlpRulesManager::Level::kWarn))
+      .WillOnce(testing::Return(DlpRulesManager::Level::kAllow));
+
+  auto dst_url = mount_points->CreateExternalFileSystemURL(
+      blink::StorageKey(), "removable",
+      base::FilePath("MyUSB/path/in/removable"));
+  ASSERT_TRUE(dst_url.is_valid());
+
+  files_controller_.IsFilesTransferRestricted(
+      profile_.get(), files_sources, dst_url.path().value(),
+      DlpWarnDialog::FilesAction::kDownload, cb.Get());
+
+  storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/printing/calculators_policies_binder.cc b/chrome/browser/ash/printing/calculators_policies_binder.cc
index d52d515..4c4aba2 100644
--- a/chrome/browser/ash/printing/calculators_policies_binder.cc
+++ b/chrome/browser/ash/printing/calculators_policies_binder.cc
@@ -35,13 +35,10 @@
   return BulkPrintersCalculator::ALL_ACCESS;
 }
 
-std::vector<std::string> ConvertToVector(const base::Value* list) {
+std::vector<std::string> ConvertToVector(const base::Value::List& list) {
   std::vector<std::string> string_list;
-  if (!list || !list->is_list()) {
-    return string_list;
-  }
 
-  for (const base::Value& value : list->GetList()) {
+  for (const base::Value& value : list) {
     if (value.is_string()) {
       string_list.push_back(value.GetString());
     }
@@ -72,7 +69,7 @@
   }
 
   std::vector<std::string> GetStringList(const char* name) const override {
-    return ConvertToVector(prefs_->GetList(name));
+    return ConvertToVector(prefs_->GetValueList(name));
   }
 
  private:
@@ -107,7 +104,9 @@
   }
 
   std::vector<std::string> GetStringList(const char* name) const override {
-    return ConvertToVector(settings_->GetPref(name));
+    const base::Value* pref = settings_->GetPref(name);
+    return pref && pref->is_list() ? ConvertToVector(pref->GetList())
+                                   : std::vector<std::string>();
   }
 
  private:
diff --git a/chrome/browser/ash/printing/enterprise_printers_provider.cc b/chrome/browser/ash/printing/enterprise_printers_provider.cc
index 32b25a5..4c5acc0 100644
--- a/chrome/browser/ash/printing/enterprise_printers_provider.cc
+++ b/chrome/browser/ash/printing/enterprise_printers_provider.cc
@@ -29,13 +29,11 @@
 
 namespace {
 
-std::vector<std::string> ConvertToVector(const base::Value* list) {
+std::vector<std::string> ConvertToVector(const base::Value::List& list) {
   std::vector<std::string> string_list;
-  if (list && list->is_list()) {
-    for (const base::Value& value : list->GetList()) {
-      if (value.is_string()) {
-        string_list.push_back(value.GetString());
-      }
+  for (const base::Value& value : list) {
+    if (value.is_string()) {
+      string_list.push_back(value.GetString());
     }
   }
   return string_list;
@@ -229,7 +227,7 @@
 
   // Extracts the list of strings named |policy_name| from user policies.
   std::vector<std::string> FromPrefs(const std::string& policy_name) {
-    return ConvertToVector(profile_->GetPrefs()->GetList(policy_name));
+    return ConvertToVector(profile_->GetPrefs()->GetValueList(policy_name));
   }
 
   // Checks if given policy is set and if it is a dictionary
diff --git a/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.cc b/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.cc
index 4a8cfd7..4e321876 100644
--- a/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.cc
@@ -78,6 +78,10 @@
   return bounds;
 }
 
+bool FacialMLSystemAppDelegate::IsAppEnabled() const {
+  return base::FeatureList::IsEnabled(ash::features::kFaceMlApp);
+}
+
 bool FacialMLSystemAppDelegate::ShouldCaptureNavigations() const {
   return true;
 }
diff --git a/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.h b/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.h
index a6574231..2fbc52b 100644
--- a/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.h
+++ b/chrome/browser/ash/web_applications/facial_ml_system_web_app_info.h
@@ -20,6 +20,7 @@
   // ash::SystemWebAppDelegate overrides:
   std::unique_ptr<WebAppInstallInfo> GetWebAppInfo() const override;
   gfx::Rect GetDefaultBounds(Browser*) const override;
+  bool IsAppEnabled() const override;
   bool ShouldCaptureNavigations() const override;
   bool ShouldReuseExistingWindow() const override;
   bool ShouldShowNewWindowMenuOption() const override;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
index 0f8c1d3..b84607f 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
@@ -182,8 +182,10 @@
 }
 
 void OnSetDlpFilesPolicy(const ::dlp::SetDlpFilesPolicyResponse response) {
-  if (response.has_error_message()) {
+  if (DlpScopedFileAccessDelegate::HasInstance()) {
     DlpScopedFileAccessDelegate::DeleteInstance();
+  }
+  if (response.has_error_message()) {
     LOG(ERROR) << "Failed to set DLP Files policy and start DLP daemon, error: "
                << response.error_message();
     return;
@@ -207,7 +209,9 @@
 
 DlpRulesManagerImpl::~DlpRulesManagerImpl() {
   DataTransferDlpController::DeleteInstance();
-  DlpScopedFileAccessDelegate::DeleteInstance();
+  if (DlpScopedFileAccessDelegate::HasInstance()) {
+    DlpScopedFileAccessDelegate::DeleteInstance();
+  }
 }
 
 // static
@@ -611,7 +615,7 @@
     DlpBooleanHistogram(dlp::kFilesDaemonStartedUMA, true);
     chromeos::DlpClient::Get()->SetDlpFilesPolicy(
         request_to_daemon, base::BindOnce(&OnSetDlpFilesPolicy));
-  } else {
+  } else if (DlpScopedFileAccessDelegate::HasInstance()) {
     DlpScopedFileAccessDelegate::DeleteInstance();
   }
 }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc
index 363ff619..9c573f0 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h"
 
 #include <sys/stat.h>
+#include <cstddef>
 
 #include "base/process/process_handle.h"
 #include "chromeos/dbus/dlp/dlp_client.h"
@@ -30,6 +31,11 @@
 }
 
 // static
+bool DlpScopedFileAccessDelegate::HasInstance() {
+  return g_delegate;
+}
+
+// static
 void DlpScopedFileAccessDelegate::Initialize(chromeos::DlpClient* client) {
   g_delegate = new DlpScopedFileAccessDelegate(client);
 }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h
index a5f2bda..ac339f5 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h
@@ -38,6 +38,9 @@
   // Deletes the singleton instance.
   static void DeleteInstance();
 
+  // Checks if the singleton is initialized
+  static bool HasInstance();
+
   // Requests access to |files| in order to be sent to |destination_url|.
   // |continuation_callback| is called with a token that should be hold until
   // `open()` operation on the files finished.
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.cc b/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.cc
index a63df61..28fcc5ce 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.cc
@@ -10,6 +10,7 @@
 
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_confidential_contents.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -81,8 +82,8 @@
 
 // Returns the OK button label for |restriction|.
 const std::u16string GetDialogButtonOkLabel(
-    DlpWarnDialog::Restriction restriction) {
-  switch (restriction) {
+    DlpWarnDialog::DlpWarnDialogOptions options) {
+  switch (options.restriction) {
     case DlpWarnDialog::Restriction::kScreenCapture:
       return l10n_util::GetStringUTF16(
           IDS_POLICY_DLP_SCREEN_CAPTURE_WARN_CONTINUE_BUTTON);
@@ -95,6 +96,10 @@
     case DlpWarnDialog::Restriction::kScreenShare:
       return l10n_util::GetStringUTF16(
           IDS_POLICY_DLP_SCREEN_SHARE_WARN_CONTINUE_BUTTON);
+    case DlpWarnDialog::Restriction::kFiles:
+      DCHECK(options.files_action.has_value());
+      // TODO(crbug.com/1350936) Add proper strings to the dialog
+      return u"Transfer anyway";
   }
 }
 
@@ -114,12 +119,15 @@
     case DlpWarnDialog::Restriction::kScreenShare:
       return l10n_util::GetStringUTF16(
           IDS_POLICY_DLP_SCREEN_SHARE_WARN_CANCEL_BUTTON);
+    case DlpWarnDialog::Restriction::kFiles:
+      // TODO(crbug.com/1350936) Add proper strings to the dialog
+      return u"Cancel";
   }
 }
 
 // Returns the title for |restriction|.
-const std::u16string GetTitle(DlpWarnDialog::Restriction restriction) {
-  switch (restriction) {
+const std::u16string GetTitle(DlpWarnDialog::DlpWarnDialogOptions options) {
+  switch (options.restriction) {
     case DlpWarnDialog::Restriction::kScreenCapture:
       return l10n_util::GetStringUTF16(
           IDS_POLICY_DLP_SCREEN_CAPTURE_WARN_TITLE);
@@ -129,6 +137,10 @@
       return l10n_util::GetStringUTF16(IDS_POLICY_DLP_PRINTING_WARN_TITLE);
     case DlpWarnDialog::Restriction::kScreenShare:
       return l10n_util::GetStringUTF16(IDS_POLICY_DLP_SCREEN_SHARE_WARN_TITLE);
+    case DlpWarnDialog::Restriction::kFiles:
+      DCHECK(options.files_action.has_value());
+      // TODO(crbug.com/1350936) Add proper strings to the dialog
+      return u"Transfer confidential file(s)?";
   }
 }
 
@@ -148,6 +160,11 @@
       return l10n_util::GetStringFUTF16(
           IDS_POLICY_DLP_SCREEN_SHARE_WARN_MESSAGE,
           options.application_title.value());
+    case DlpWarnDialog::Restriction::kFiles:
+      DCHECK(options.files_action.has_value());
+      // TODO(crbug.com/1350936) Add proper strings to the dialog
+      return u"Administrator policy doesn’t recommend transfering this/these "
+             u"file(s) to destination";
   }
 }
 
@@ -184,7 +201,7 @@
                                                kManagedIconSize, color));
 
   views::Label* title_label = upper_panel->AddChildView(
-      std::make_unique<views::Label>(GetTitle(options.restriction)));
+      std::make_unique<views::Label>(GetTitle(options)));
   title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   title_label->SetAllowCharacterBreak(true);
 // TODO(crbug.com/1261496) Enable dynamic UI color & theme in lacros
@@ -309,6 +326,11 @@
 }
 
 DlpWarnDialog::DlpWarnDialogOptions::DlpWarnDialogOptions(
+    Restriction restriction,
+    FilesAction files_action)
+    : restriction(restriction), files_action(files_action) {}
+
+DlpWarnDialog::DlpWarnDialogOptions::DlpWarnDialogOptions(
     const DlpWarnDialogOptions& other) = default;
 
 DlpWarnDialog::DlpWarnDialogOptions&
@@ -326,8 +348,7 @@
   SetModalType(ui::MODAL_TYPE_SYSTEM);
 
   SetShowCloseButton(false);
-  SetButtonLabel(ui::DIALOG_BUTTON_OK,
-                 GetDialogButtonOkLabel(options.restriction));
+  SetButtonLabel(ui::DIALOG_BUTTON_OK, GetDialogButtonOkLabel(options));
   SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
                  GetDialogButtonCancelLabel(options.restriction));
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h b/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h
index 488cd3e..38fae7e 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h
@@ -34,9 +34,14 @@
     kScreenCapture,
     kVideoCapture,
     kPrinting,
-    kScreenShare
+    kScreenShare,
+    kFiles
   };
 
+  // Type of the files action for which the dialog with kFiles restriction is
+  // created, used to determine the text shown in the dialog.
+  enum class FilesAction { kDownload, kTransfer };
+
   // A structure to keep track of optional and configurable parameters of a
   // DlpWarnDialog.
   struct DlpWarnDialogOptions {
@@ -47,6 +52,7 @@
     DlpWarnDialogOptions(Restriction restriction,
                          DlpConfidentialContents confidential_contents,
                          const std::u16string& application_title);
+    DlpWarnDialogOptions(Restriction restriction, FilesAction files_action);
     DlpWarnDialogOptions(const DlpWarnDialogOptions& other);
     DlpWarnDialogOptions& operator=(const DlpWarnDialogOptions& other);
     ~DlpWarnDialogOptions();
@@ -68,6 +74,9 @@
     Restriction restriction;
     DlpConfidentialContents confidential_contents;
     absl::optional<std::u16string> application_title;
+
+    // Has value only if the |restriction| is kFiles.
+    absl::optional<FilesAction> files_action;
   };
 
   DlpWarnDialog() = delete;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.cc b/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.cc
index 0ef6d23..1ef21d7b 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.cc
@@ -52,6 +52,14 @@
           DlpWarnDialog::Restriction::kVideoCapture, confidential_contents));
 }
 
+void DlpWarnNotifier::ShowDlpFilesWarningDialog(
+    OnDlpRestrictionCheckedCallback callback,
+    DlpWarnDialog::FilesAction files_action) {
+  ShowDlpWarningDialog(std::move(callback),
+                       DlpWarnDialog::DlpWarnDialogOptions(
+                           DlpWarnDialog::Restriction::kFiles, files_action));
+}
+
 base::WeakPtr<views::Widget> DlpWarnNotifier::ShowDlpScreenShareWarningDialog(
     OnDlpRestrictionCheckedCallback callback,
     const DlpConfidentialContents& confidential_contents,
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h b/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h
index a4a43c40..f66e31f 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_warn_notifier.h
@@ -46,6 +46,12 @@
       OnDlpRestrictionCheckedCallback callback,
       const DlpConfidentialContents& confidential_contents);
 
+  // Shows a warning dialog that informs the user that |files_action| on
+  // selected files is not recommended. Calls |callback| and passes user's
+  // choice of whether to proceed or not.
+  void ShowDlpFilesWarningDialog(OnDlpRestrictionCheckedCallback callback,
+                                 DlpWarnDialog::FilesAction files_action);
+
   // Shows a warning dialog that informs the user that screen sharing is not
   // recommended due to |confidential_contents| visible. Calls |callback| and
   // passes user's choice of whether to proceed or not.
diff --git a/chrome/browser/download/bubble/download_bubble_controller.cc b/chrome/browser/download/bubble/download_bubble_controller.cc
index 8fe6e4e..f9d5f9b 100644
--- a/chrome/browser/download/bubble/download_bubble_controller.cc
+++ b/chrome/browser/download/bubble/download_bubble_controller.cc
@@ -118,6 +118,10 @@
   display_controller_->HideToolbarButton();
 }
 
+void DownloadBubbleUIController::HandleButtonPressed() {
+  display_controller_->HandleButtonPressed();
+}
+
 bool DownloadBubbleUIController::MaybeAddOfflineItem(const OfflineItem& item,
                                                      bool is_new) {
   if (profile_->IsOffTheRecord() != item.is_off_the_record)
diff --git a/chrome/browser/download/bubble/download_bubble_controller.h b/chrome/browser/download/bubble/download_bubble_controller.h
index 1dec973..963dad5 100644
--- a/chrome/browser/download/bubble/download_bubble_controller.h
+++ b/chrome/browser/download/bubble/download_bubble_controller.h
@@ -81,6 +81,9 @@
   // this controller belongs to should show the partial view.
   void OnNewItem(download::DownloadItem* item, bool show_details);
 
+  // Notify when a download toolbar button (in any window) is pressed.
+  void HandleButtonPressed();
+
   // Returns whether the incognito icon should be shown for the download.
   bool ShouldShowIncognitoIcon(const DownloadUIModel* model) const;
 
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc
index 100aeb3..58844ac5 100644
--- a/chrome/browser/download/bubble/download_display_controller.cc
+++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -10,8 +10,11 @@
 #include "chrome/browser/download/bubble/download_bubble_prefs.h"
 #include "chrome/browser/download/bubble/download_display.h"
 #include "chrome/browser/download/bubble/download_icon_state.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/download/download_ui_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/download/download_item_mode.h"
@@ -156,6 +159,16 @@
 }
 
 void DownloadDisplayController::OnButtonPressed() {
+  DownloadUIController* download_ui_controller =
+      DownloadCoreServiceFactory::GetForBrowserContext(
+          browser_->profile()->GetOriginalProfile())
+          ->GetDownloadUIController();
+  if (download_ui_controller) {
+    download_ui_controller->OnButtonClicked();
+  }
+}
+
+void DownloadDisplayController::HandleButtonPressed() {
   // If the current state is kComplete, set the icon to inactive because of the
   // user action.
   if (icon_info_.icon_state == DownloadIconState::kComplete) {
diff --git a/chrome/browser/download/bubble/download_display_controller.h b/chrome/browser/download/bubble/download_display_controller.h
index 36ec649..5473708 100644
--- a/chrome/browser/download/bubble/download_display_controller.h
+++ b/chrome/browser/download/bubble/download_display_controller.h
@@ -69,6 +69,9 @@
   // Notifies the controller that the button is pressed. Called by `display_`.
   void OnButtonPressed();
 
+  // Handles the button pressed event. Called by the profile level controller.
+  void HandleButtonPressed();
+
   // Common methods for new downloads or new offline items.
   // Called from bubble controller when new item(s) are added, with
   // |show_details| as argument if the partial view should be shown.
diff --git a/chrome/browser/download/bubble/download_display_controller_unittest.cc b/chrome/browser/download/bubble/download_display_controller_unittest.cc
index 7f1d881d0..3ea29d9 100644
--- a/chrome/browser/download/bubble/download_display_controller_unittest.cc
+++ b/chrome/browser/download/bubble/download_display_controller_unittest.cc
@@ -747,7 +747,7 @@
                                  /*icon_state=*/DownloadIconState::kComplete,
                                  /*is_active=*/true));
 
-  controller().OnButtonPressed();
+  controller().HandleButtonPressed();
 
   EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/true,
                                  /*icon_state=*/DownloadIconState::kComplete,
diff --git a/chrome/browser/download/download_core_service.h b/chrome/browser/download/download_core_service.h
index 59ceb6b..547fb33 100644
--- a/chrome/browser/download/download_core_service.h
+++ b/chrome/browser/download/download_core_service.h
@@ -12,6 +12,7 @@
 #include "extensions/buildflags/buildflags.h"
 
 class ChromeDownloadManagerDelegate;
+class DownloadUIController;
 class ExtensionDownloadsEventRouter;
 
 namespace content {
@@ -36,6 +37,9 @@
   // Get the download manager delegate, creating it if it doesn't already exist.
   virtual ChromeDownloadManagerDelegate* GetDownloadManagerDelegate() = 0;
 
+  // Get the download UI controller, return nullptr if it doesn't already exist.
+  virtual DownloadUIController* GetDownloadUIController() = 0;
+
   // Get the interface to the history system. Returns NULL if profile is
   // incognito or if the DownloadManager hasn't been created yet or if there is
   // no HistoryService for profile. Virtual for testing.
diff --git a/chrome/browser/download/download_core_service_impl.cc b/chrome/browser/download/download_core_service_impl.cc
index 11c2135..0cfafcf 100644
--- a/chrome/browser/download/download_core_service_impl.cc
+++ b/chrome/browser/download/download_core_service_impl.cc
@@ -94,6 +94,10 @@
   return manager_delegate_.get();
 }
 
+DownloadUIController* DownloadCoreServiceImpl::GetDownloadUIController() {
+  return download_ui_ ? download_ui_.get() : nullptr;
+}
+
 DownloadHistory* DownloadCoreServiceImpl::GetDownloadHistory() {
   if (!download_manager_created_) {
     GetDownloadManagerDelegate();
diff --git a/chrome/browser/download/download_core_service_impl.h b/chrome/browser/download/download_core_service_impl.h
index 8456a98..fa9f335 100644
--- a/chrome/browser/download/download_core_service_impl.h
+++ b/chrome/browser/download/download_core_service_impl.h
@@ -54,6 +54,7 @@
       std::unique_ptr<ChromeDownloadManagerDelegate> delegate) override;
   bool IsDownloadUiEnabled() override;
   bool IsDownloadObservedByExtension() override;
+  DownloadUIController* GetDownloadUIController() override;
   void SetDownloadHistoryForTesting(
       std::unique_ptr<DownloadHistory> download_history) override;
 
diff --git a/chrome/browser/download/download_file_picker.cc b/chrome/browser/download/download_file_picker.cc
index e5e030c..f5845b46 100644
--- a/chrome/browser/download/download_file_picker.cc
+++ b/chrome/browser/download/download_file_picker.cc
@@ -22,6 +22,7 @@
 #include "ui/aura/window.h"
 #elif BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/policy/dlp/dlp_files_controller.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h"
 #include "chrome/browser/profiles/profile.h"
 #endif
 
@@ -119,6 +120,7 @@
     dlp_files_controller_->IsFilesTransferRestricted(
         Profile::FromBrowserContext(web_contents->GetBrowserContext()),
         {download_item_->GetURL()}, path.value(),
+        policy::DlpWarnDialog::FilesAction::kDownload,
         base::BindOnce(&DownloadFilePicker::CompleteFileSelection,
                        base::Unretained(this), path));
     return;
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc
index d3b80277..2b83943 100644
--- a/chrome/browser/download/download_ui_controller.cc
+++ b/chrome/browser/download/download_ui_controller.cc
@@ -129,6 +129,7 @@
  private:
   // DownloadUIController::Delegate
   void OnNewDownloadReady(download::DownloadItem* item) override;
+  void OnButtonClicked() override;
 
   raw_ptr<Profile> profile_;
 };
@@ -172,6 +173,19 @@
   }
 }
 
+void DownloadBubbleUIControllerDelegate::OnButtonClicked() {
+  BrowserList* browser_list = BrowserList::GetInstance();
+  if (!browser_list)
+    return;
+
+  for (auto* browser : *browser_list) {
+    if (browser && browser->window() &&
+        browser->window()->GetDownloadBubbleUIController()) {
+      browser->window()->GetDownloadBubbleUIController()->HandleButtonPressed();
+    }
+  }
+}
+
 #endif  // BUILDFLAG(IS_ANDROID)
 
 } // namespace
@@ -179,6 +193,8 @@
 DownloadUIController::Delegate::~Delegate() {
 }
 
+void DownloadUIController::Delegate::OnButtonClicked() {}
+
 DownloadUIController::DownloadUIController(content::DownloadManager* manager,
                                            std::unique_ptr<Delegate> delegate)
     : download_notifier_(manager, this), delegate_(std::move(delegate)) {
@@ -213,6 +229,10 @@
 DownloadUIController::~DownloadUIController() {
 }
 
+void DownloadUIController::OnButtonClicked() {
+  delegate_->OnButtonClicked();
+}
+
 void DownloadUIController::OnDownloadCreated(content::DownloadManager* manager,
                                              download::DownloadItem* item) {
   // Record the security level of the page triggering the download. Only record
diff --git a/chrome/browser/download/download_ui_controller.h b/chrome/browser/download/download_ui_controller.h
index 6206fbb..8f8c63b 100644
--- a/chrome/browser/download/download_ui_controller.h
+++ b/chrome/browser/download/download_ui_controller.h
@@ -25,6 +25,10 @@
     // This method is invoked to notify the UI of the new download |item|. Note
     // that |item| may be in any state by the time this method is invoked.
     virtual void OnNewDownloadReady(download::DownloadItem* item) = 0;
+
+    // Notifies the controller that the main download button is clicked. Only
+    // invoked by the download bubble UI.
+    virtual void OnButtonClicked();
   };
 
   // |manager| is the download manager to observe for new downloads. If
@@ -42,6 +46,10 @@
 
   ~DownloadUIController() override;
 
+  // Notifies the controller that the main download button is clicked. Currently
+  // only invoked by the download bubble UI.
+  void OnButtonClicked();
+
  private:
   void OnDownloadCreated(content::DownloadManager* manager,
                          download::DownloadItem* item) override;
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 4e2acc7..45c3d7b 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -312,6 +312,16 @@
   (*s_allowlist)[::prefs::kHttpsOnlyModeEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
+  // Cookies page
+  (*s_allowlist)[::content_settings::kCookiePrimarySetting] =
+      settings_api::PrefType::PREF_TYPE_NUMBER;
+  (*s_allowlist)[::content_settings::kCookieSessionOnly] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_allowlist)[::prefs::kCookieControlsMode] =
+      settings_api::PrefType::PREF_TYPE_NUMBER;
+  (*s_allowlist)[::prefs::kPrivacySandboxFirstPartySetsDataAccessAllowed] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
+
   // Sync and personalization page.
   (*s_allowlist)[::prefs::kSearchSuggestEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
@@ -391,12 +401,6 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
   // Site Settings prefs.
-  (*s_allowlist)[::content_settings::kCookiePrimarySetting] =
-      settings_api::PrefType::PREF_TYPE_NUMBER;
-  (*s_allowlist)[::content_settings::kCookieSessionOnly] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
-  (*s_allowlist)[::prefs::kCookieControlsMode] =
-      settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_allowlist)[::content_settings::kGeneratedNotificationPref] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_allowlist)[::prefs::kPluginsAlwaysOpenPdfExternally] =
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index cccb3b90..6688f01 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -32,6 +32,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/file_system_access/file_system_access_permission_context_factory.h"
 #include "chrome/browser/file_system_access/file_system_access_permission_request_manager.h"
 #include "chrome/browser/installable/installable_utils.h"
@@ -40,6 +41,7 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ui/file_system_access_dialogs.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/pdf_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -1361,8 +1363,20 @@
 
 base::FilePath
 ChromeFileSystemAccessPermissionContext::GetWellKnownDirectoryPath(
-    blink::mojom::WellKnownDirectory directory) {
+    blink::mojom::WellKnownDirectory directory,
+    const url::Origin& origin) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // PDF viewer uses the default Download directory set in browser, if possible.
+  if (directory == blink::mojom::WellKnownDirectory::kDirDownloads &&
+      IsPdfExtensionOrigin(origin)) {
+    base::FilePath profile_download_path =
+        DownloadPrefs::FromBrowserContext(profile())->DownloadPath();
+    if (!profile_download_path.empty()) {
+      return profile_download_path;
+    }
+  }
+
   int key = base::PATH_START;
   switch (directory) {
     case blink::mojom::WellKnownDirectory::kDefault:
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
index 5f17f16..6c41f51 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
@@ -102,7 +102,8 @@
   PathInfo GetLastPickedDirectory(const url::Origin& origin,
                                   const std::string& id) override;
   base::FilePath GetWellKnownDirectoryPath(
-      blink::mojom::WellKnownDirectory directory) override;
+      blink::mojom::WellKnownDirectory directory,
+      const url::Origin& origin) override;
 
   std::u16string GetPickerTitle(
       const blink::mojom::FilePickerOptionsPtr& options) override;
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index 4caaaa7e..df56a1b9 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -22,6 +22,10 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/file_system_access/file_system_access_permission_request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_paths.h"
@@ -87,6 +91,11 @@
   }
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    DownloadCoreServiceFactory::GetForBrowserContext(profile())
+        ->SetDownloadManagerDelegateForTesting(
+            std::make_unique<ChromeDownloadManagerDelegate>(profile()));
+
     web_contents_ =
         content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
     FileSystemAccessPermissionRequestManager::CreateForWebContents(
@@ -167,6 +176,8 @@
       url::Origin::Create(GURL("https://example.com"));
   const url::Origin kTestOrigin2 =
       url::Origin::Create(GURL("https://test.com"));
+  const url::Origin kPdfOrigin = url::Origin::Create(
+      GURL("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html"));
   const std::string kTestStartingDirectoryId = "test_id";
   const base::FilePath kTestPath =
       base::FilePath(FILE_PATH_LITERAL("/foo/bar"));
@@ -689,7 +700,7 @@
   base::ScopedPathOverride user_desktop_override(
       base::DIR_USER_DESKTOP, temp_dir_.GetPath(), true, true);
   EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
-                blink::mojom::WellKnownDirectory::kDirDesktop),
+                blink::mojom::WellKnownDirectory::kDirDesktop, kTestOrigin),
             temp_dir_.GetPath());
 }
 
@@ -698,7 +709,7 @@
   base::ScopedPathOverride user_documents_override(
       chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
   EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
-                blink::mojom::WellKnownDirectory::kDirDocuments),
+                blink::mojom::WellKnownDirectory::kDirDocuments, kTestOrigin),
             temp_dir_.GetPath());
 }
 
@@ -707,7 +718,18 @@
   base::ScopedPathOverride user_documents_override(
       chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
   EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
-                blink::mojom::WellKnownDirectory::kDefault),
+                blink::mojom::WellKnownDirectory::kDefault, kTestOrigin),
+            temp_dir_.GetPath());
+}
+
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       GetWellKnownDirectoryPath_Pdf_Downloads) {
+  DownloadPrefs::FromBrowserContext(browser_context())
+      ->SkipSanitizeDownloadTargetPathForTesting();
+  DownloadPrefs::FromBrowserContext(browser_context())
+      ->SetDownloadPath(temp_dir_.GetPath());
+  EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
+                blink::mojom::WellKnownDirectory::kDirDownloads, kPdfOrigin),
             temp_dir_.GetPath());
 }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e76a19e..4bd05c5 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -764,6 +764,11 @@
     "expiry_milestone": 110
   },
   {
+    "name": "bruschetta-alpha-migrate",
+    "owners": [ "davidmunro@google.com", "nverne", "sidereal" ],
+    "expiry_milestone": 114
+  },
+  {
     "name": "bubble-rich-iph",
     "owners": ["christianxu", "lpromero", "bling-flags@google.com"],
     "expiry_milestone": 106
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index ff97697a..619a725c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4861,6 +4861,11 @@
 const char kBruschettaDescription[] =
     "Enables UI support for third party/generic VMs";
 
+const char kBruschettaAlphaMigrateName[] = "Migration for Bruschetta Alpha";
+const char kBruschettaAlphaMigrateDescription[] =
+    "Enable this flag to migrate a Bruschetta installed during the alpha. "
+    "Requires the bruschetta flag to be enabled.";
+
 const char kCameraAppDocScanDlcName[] = "Camera App Doc Scan DLC";
 const char kCameraAppDocScanDlcDescription[] =
     "Enables this flag to allow downloading document scanning feature via DLC "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index fa13cae..58261fd 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2786,6 +2786,9 @@
 extern const char kBruschettaName[];
 extern const char kBruschettaDescription[];
 
+extern const char kBruschettaAlphaMigrateName[];
+extern const char kBruschettaAlphaMigrateDescription[];
+
 extern const char kCameraAppDocScanDlcName[];
 extern const char kCameraAppDocScanDlcDescription[];
 
diff --git a/chrome/browser/media/media_foundation_service_monitor.cc b/chrome/browser/media/media_foundation_service_monitor.cc
index 8603bdf..3677ad2 100644
--- a/chrome/browser/media/media_foundation_service_monitor.cc
+++ b/chrome/browser/media/media_foundation_service_monitor.cc
@@ -54,8 +54,7 @@
 
   std::vector<base::Time> times;
   for (const base::Value& time_value :
-       service->GetList(prefs::kHardwareSecureDecryptionDisabledTimes)
-           ->GetListDeprecated()) {
+       service->GetValueList(prefs::kHardwareSecureDecryptionDisabledTimes)) {
     auto time = base::ValueToTime(time_value);
     if (time.has_value())
       times.push_back(time.value());
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index 933c2cd9..c414348 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -203,12 +203,12 @@
     "../../components/dialogs:oobe_loading_dialog.m",
     "../../components/dialogs:oobe_modal_dialog.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
   ]
   extra_deps = [ ":consolidated_consent_module" ]
   externs_list = [
     "$externs_path/chrome_extensions.js",
     "$externs_path/webview_tag.js",
+    "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js",
   ]
 }
 
@@ -317,12 +317,12 @@
     "../../components/dialogs:oobe_loading_dialog.m",
     "../../components/dialogs:oobe_modal_dialog.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
   ]
   extra_deps = [ ":guest_tos_module" ]
   externs_list = [
     "$externs_path/chrome_extensions.js",
     "$externs_path/webview_tag.js",
+    "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js",
   ]
 }
 
@@ -369,8 +369,9 @@
     "../../components/behaviors:oobe_i18n_behavior.m",
     "../../components/dialogs:oobe_adaptive_dialog.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":marketing_opt_in_module" ]
 }
 
@@ -403,9 +404,10 @@
     "../../components/buttons:oobe_text_button.m",
     "../../components/dialogs:oobe_adaptive_dialog.m",
     "//ui/webui/resources/cr_elements/cr_input:cr_input",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:load_time_data.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":offline_ad_login_module" ]
 }
 
@@ -489,10 +491,12 @@
     "../../components/dialogs:oobe_adaptive_dialog.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_lottie:cr_lottie.m",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
   ]
 
-  externs_list = [ "$externs_path/webview_tag.js" ]
+  externs_list = [
+    "$externs_path/webview_tag.js",
+    "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js",
+  ]
 
   extra_deps = [ ":os_trial_module" ]
 }
@@ -625,9 +629,10 @@
     "../../components/behaviors:oobe_i18n_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_lottie:cr_lottie.m",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:cr.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":user_creation_module" ]
 }
 
@@ -781,7 +786,7 @@
   js_file = "marketing_opt_in.js"
   html_file = "marketing_opt_in.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports + [ "ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html|CrToggleElement" ]
+  auto_imports = oobe_auto_imports
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
@@ -798,7 +803,7 @@
   js_file = "offline_ad_login.js"
   html_file = "offline_ad_login.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports + [ "ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html|CrToggleElement" ]
+  auto_imports = oobe_auto_imports
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
@@ -876,7 +881,9 @@
   js_file = "saml_confirm_password.js"
   html_file = "saml_confirm_password.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports
+  auto_imports =
+      oobe_auto_imports +
+      [ "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement" ]
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
diff --git a/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn
index 04242c0a..7436bad 100644
--- a/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/login/BUILD.gn
@@ -153,7 +153,9 @@
   js_file = "active_directory_password_change.js"
   html_file = "active_directory_password_change.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports
+  auto_imports =
+      oobe_auto_imports +
+      [ "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement" ]
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
@@ -162,7 +164,9 @@
   js_file = "gaia_password_changed.js"
   html_file = "gaia_password_changed.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports
+  auto_imports =
+      oobe_auto_imports +
+      [ "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement" ]
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
@@ -187,7 +191,10 @@
   js_file = "offline_login.js"
   html_file = "offline_login.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports + [ "ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html|CrDialogElement" ]
+  auto_imports = oobe_auto_imports + [
+                   "ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html|CrDialogElement",
+                   "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement",
+                 ]
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
@@ -209,7 +216,5 @@
 }
 
 html_to_js("web_components") {
-  js_files = [
-    "encryption_migration.js",
-  ]
-}
\ No newline at end of file
+  js_files = [ "encryption_migration.js" ]
+}
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
index a2effce9..3879cf2 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
@@ -248,7 +248,9 @@
   js_file = "enable_debugging.js"
   html_file = "enable_debugging.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports
+  auto_imports =
+      oobe_auto_imports +
+      [ "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement" ]
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
@@ -307,7 +309,9 @@
   js_file = "welcome.js"
   html_file = "welcome.html"
   html_type = "dom-module"
-  auto_imports = oobe_auto_imports
+  auto_imports =
+      oobe_auto_imports +
+      [ "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement" ]
   migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
diff --git a/chrome/browser/resources/inline_login/BUILD.gn b/chrome/browser/resources/inline_login/BUILD.gn
index 08d36b5..af9b4fc7 100644
--- a/chrome/browser/resources/inline_login/BUILD.gn
+++ b/chrome/browser/resources/inline_login/BUILD.gn
@@ -83,7 +83,6 @@
 js_library("welcome_page_app") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:load_time_data.m",
   ]
diff --git a/chrome/browser/resources/password_manager/BUILD.gn b/chrome/browser/resources/password_manager/BUILD.gn
index 9926ada..427ef55 100644
--- a/chrome/browser/resources/password_manager/BUILD.gn
+++ b/chrome/browser/resources/password_manager/BUILD.gn
@@ -7,9 +7,18 @@
 build_webui("build") {
   grd_prefix = "password_manager"
   static_files = [ "password_manager.html" ]
-  web_component_files = [ "password_manager_app.ts" ]
+  web_component_files = [
+    "password_manager_app.ts",
+    "toolbar.ts",
+  ]
   non_web_component_files = [ "password_manager.ts" ]
 
+  # Files that are passed as input to css_to_wrapper().
+  css_files = [
+    "shared_style.css",
+    "shared_vars.css",
+  ]
+
   ts_deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
diff --git a/chrome/browser/resources/password_manager/password_manager.ts b/chrome/browser/resources/password_manager/password_manager.ts
index 6eb53172..1090b71 100644
--- a/chrome/browser/resources/password_manager/password_manager.ts
+++ b/chrome/browser/resources/password_manager/password_manager.ts
@@ -5,3 +5,4 @@
 import './password_manager_app.js';
 
 export {PasswordManagerAppElement} from './password_manager_app.js';
+export {PasswordManagerToolbarElement} from './toolbar.js';
diff --git a/chrome/browser/resources/password_manager/password_manager_app.html b/chrome/browser/resources/password_manager/password_manager_app.html
index 5fc32f02..2410bbc 100644
--- a/chrome/browser/resources/password_manager/password_manager_app.html
+++ b/chrome/browser/resources/password_manager/password_manager_app.html
@@ -1 +1 @@
-Password Manager page coming soon
+<password-manager-toolbar id="toolbar"></password-manager-toolbar>
diff --git a/chrome/browser/resources/password_manager/shared_style.css b/chrome/browser/resources/password_manager/shared_style.css
new file mode 100644
index 0000000..f1eb06c3
--- /dev/null
+++ b/chrome/browser/resources/password_manager/shared_style.css
@@ -0,0 +1,10 @@
+/* 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. */
+
+/* #css_wrapper_metadata_start
+ * #type=style
+ * #import=./shared_vars.css.js
+ * #import=chrome://resources/cr_elements/hidden_style_css.m.js
+ * #import=chrome://resources/cr_elements/shared_vars_css.m.js
+ * #css_wrapper_metadata_end */
diff --git a/chrome/browser/resources/password_manager/shared_vars.css b/chrome/browser/resources/password_manager/shared_vars.css
new file mode 100644
index 0000000..8780b4a
--- /dev/null
+++ b/chrome/browser/resources/password_manager/shared_vars.css
@@ -0,0 +1,13 @@
+/* Copyright 2022 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=vars
+ * #css_wrapper_metadata_end */
+
+html {
+  --card-max-width: 960px;
+  --side-bar-width: 256px;
+  --toolbar-height: 56px;
+}
diff --git a/chrome/browser/resources/password_manager/toolbar.html b/chrome/browser/resources/password_manager/toolbar.html
new file mode 100644
index 0000000..8aa345e
--- /dev/null
+++ b/chrome/browser/resources/password_manager/toolbar.html
@@ -0,0 +1,21 @@
+<style include="shared-style">
+  :host {
+    display: flex;
+    position: relative;
+  }
+
+  /* General toolbar layout. */
+
+  cr-toolbar {
+    --cr-toolbar-center-basis: var(--card-max-width);
+    --cr-toolbar-left-spacer-width: var(--side-bar-width);
+    --cr-toolbar-field-margin: var(--side-bar-width);
+    --cr-toolbar-header-white-space: nowrap;
+    flex: 1;
+  }
+</style>
+<cr-toolbar id="mainToolbar" disable-right-content-grow
+    page-name="$i18n{title}" clear-label="$i18n{clearSearch}"
+    search-prompt="$i18n{searchPrompt}" autofocus>
+  <cr-icon-button iron-icon="cr:help-outline"></cr-icon-button>
+</cr-toolbar>
diff --git a/chrome/browser/resources/password_manager/toolbar.ts b/chrome/browser/resources/password_manager/toolbar.ts
new file mode 100644
index 0000000..b5475d69
--- /dev/null
+++ b/chrome/browser/resources/password_manager/toolbar.ts
@@ -0,0 +1,23 @@
+// 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 './shared_style.css.js';
+import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './toolbar.html.js';
+
+export class PasswordManagerToolbarElement extends PolymerElement {
+  static get is() {
+    return 'password-manager-toolbar';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+}
+
+customElements.define(
+    PasswordManagerToolbarElement.is, PasswordManagerToolbarElement);
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 1db5921..c098508 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -374,9 +374,7 @@
     "open_window_proxy.js",
     "people_page/profile_info_browser_proxy.js",
     "people_page/sync_browser_proxy.js",
-    "prefs/prefs.js",
     "prefs/prefs_mixin.js",
-    "prefs/prefs_types.js",
     "prefs/pref_util.js",
     "privacy_page/autofill_assistant_browser_proxy.js",
     "privacy_page/privacy_page_browser_proxy.js",
@@ -468,12 +466,8 @@
     "os_privacy_page:closure_compile_module",
     "os_reset_page:closure_compile_module",
     "os_search_page:closure_compile_module",
-    "os_settings_main:closure_compile_module",
-    "os_settings_menu:closure_compile_module",
     "os_settings_page:closure_compile_module",
     "os_settings_search_box:closure_compile_module",
-    "os_settings_ui:closure_compile_module",
-    "os_toolbar:closure_compile_module",
     "parental_controls_page:closure_compile_module",
     "personalization_page:closure_compile_module",
     "settings_scheduler_slider:closure_compile_module",
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
index 55deae52..f8401c4a6 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
@@ -267,10 +267,9 @@
    * Opens file selector dialog to allow user to select an Ansible playbook
    * to preconfigure their container.
    *
-   * @return {!Promise<string>} Returns a filepath to the selected Ansible
-   *      Playbook
+   * @return {!Promise<string>} Returns a filepath to the selected file.
    */
-  applyAnsiblePlaybook() {}
+  openContainerFileSelector() {}
 }
 
 /** @type {?CrostiniBrowserProxy} */
@@ -452,7 +451,7 @@
   }
 
   /** @override */
-  applyAnsiblePlaybook() {
-    return sendWithPromise('applyAnsiblePlaybook');
+  openContainerFileSelector() {
+    return sendWithPromise('openContainerFileSelector');
   }
 }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.html
index 75bb6c45..0167ef4 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.html
@@ -101,7 +101,7 @@
       <cr-button id="cancel" class="cancel-button" on-click="onCancelTap_">
         $i18n{cancel}
       </cr-button>
-      <cr-button id="create" class="action-button" on-click="onCreateTap_" 
+      <cr-button id="create" class="action-button" on-click="onCreateTap_"
           disabled="[[disableCreateButton_]]">
         $i18n{crostiniExtraContainersCreate}
       </cr-button>
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.js
index f340c94..6a7cc1b 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers_create_dialog.js
@@ -190,7 +190,7 @@
   /** @private */
   async onAnsiblePlaybookUploadClick_() {
     this.$.preconfiguredContainersInput.value =
-        await this.browserProxy_.applyAnsiblePlaybook();
+        await this.browserProxy_.openContainerFileSelector();
   }
 
   /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
index 0170bcd..cf85d53e 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
@@ -10,11 +10,11 @@
 
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
-import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import 'chrome://resources/cr_elements/md_select_css.m.js';
 import '../../settings_shared.css.js';
 import '../guest_os/guest_os_container_select.js';
 
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {ContainerInfo, GuestId} from '../guest_os/guest_os_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn b/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn
index 35c976d..c05382c 100644
--- a/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/guest_os/BUILD.gn
@@ -37,10 +37,11 @@
     ":guest_os_browser_proxy",
     ":guest_os_container_select",
     "..:metrics_recorder",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
 }
 
 js_library("guest_os_shared_paths") {
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
index 41e6129..34a7f539 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
@@ -115,13 +115,15 @@
     "//ui/webui/resources/cr_components/chromeos/network_health:network_health_container",
     "//ui/webui/resources/cr_components/chromeos/traffic_counters:traffic_counters",
     "//ui/webui/resources/cr_elements/cr_button:cr_button",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
-  externs_list = [ "../settings_controls_types.js" ]
+  externs_list = [
+    "../settings_controls_types.js",
+    "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js",
+  ]
 }
 
 js_library("internet_known_networks_page") {
@@ -214,10 +216,11 @@
     "//ui/webui/resources/cr_components/chromeos/network:network_listener_behavior.m",
     "//ui/webui/resources/cr_components/chromeos/network:onc_mojo.m",
     "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
 }
 
 js_library("network_always_on_vpn") {
@@ -270,11 +273,12 @@
     "//ui/webui/resources/cr_components/chromeos/network:onc_mojo.m",
     "//ui/webui/resources/cr_components/chromeos/traffic_counters:traffic_counters",
     "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/cr_elements/policy:cr_policy_indicator_behavior.m",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
 }
 
 js_library("tether_connection_dialog") {
@@ -322,12 +326,13 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider.m",
     "//ui/webui/resources/cr_components/chromeos/network:onc_mojo.m",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:load_time_data.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
 }
 
 js_library("esim_install_error_dialog") {
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js b/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js
index 76f11d9b..4341e92b 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js
@@ -13,11 +13,11 @@
 import '../../settings_shared.css.js';
 import 'chrome://resources/cr_elements/policy/cr_policy_indicator.m.js';
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 
 import {CrPolicyNetworkBehaviorMojo} from 'chrome://resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.m.js';
 import {MojoInterfaceProvider, MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
 import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
-import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js b/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js
index 9ef43d2..b30850e 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js
@@ -18,9 +18,9 @@
 import '../../settings_vars.css.js';
 import './internet_shared_css.js';
 import '../../controls/settings_toggle_button.js';
+import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 
 import {CrPolicyNetworkBehaviorMojo, CrPolicyNetworkBehaviorMojoInterface} from 'chrome://resources/cr_components/chromeos/network/cr_policy_network_behavior_mojo.m.js';
-import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
index a63fc2a..e70027c8 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
@@ -164,11 +164,12 @@
     "..:os_route",
     "../../:router",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:load_time_data.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
 }
 
 js_library("setup_fingerprint_dialog") {
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 2414598..b054eee 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -14,12 +14,12 @@
   "chromeos/os_about_page/edit_hostname_dialog.ts",
   "chromeos/os_about_page/os_about_page.ts",
   "chromeos/os_about_page/update_warning_dialog.ts",
-  "chromeos/os_settings_main/os_settings_main.js",
-  "chromeos/os_settings_menu/os_settings_menu.js",
+  "chromeos/os_settings_main/os_settings_main.ts",
+  "chromeos/os_settings_menu/os_settings_menu.ts",
   "chromeos/os_settings_page/os_settings_page.js",
   "chromeos/os_settings_page/settings_idle_load.js",
-  "chromeos/os_settings_ui/os_settings_ui.js",
-  "chromeos/os_toolbar/os_toolbar.js",
+  "chromeos/os_settings_ui/os_settings_ui.ts",
+  "chromeos/os_toolbar/os_toolbar.ts",
 ]
 
 # Files that are passed as input to html_to_wrapper().
@@ -129,6 +129,8 @@
   # Files below are from Browser Settings and shared with ChromeOS Settings
   "i18n_setup.ts",
   "lifetime_browser_proxy.ts",
+  "prefs/prefs.ts",
+  "prefs/prefs_types.ts",
   "router.js",
 ]
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_main/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_settings_main/BUILD.gn
deleted file mode 100644
index 51f630b5..0000000
--- a/chrome/browser/resources/settings/chromeos/os_settings_main/BUILD.gn
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-import("../os_settings.gni")
-
-js_type_check("closure_compile_module") {
-  closure_flags = os_settings_closure_flags
-  is_polymer3 = true
-  deps = [ ":os_settings_main" ]
-}
-
-js_library("os_settings_main") {
-  deps = [
-    "..:os_page_visibility",
-    "..:os_route",
-    "..:route_observer_behavior",
-    "../..:router",
-    "../os_settings_page:os_settings_page",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:assert.m",
-  ]
-}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.js b/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.ts
similarity index 76%
rename from chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.js
rename to chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.ts
index c66a4daa..1a6f8b2 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_main/os_settings_main.ts
@@ -17,6 +17,7 @@
 import '../../settings_shared.css.js';
 import '../../settings_vars.css.js';
 
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
@@ -27,20 +28,22 @@
 
 import {getTemplate} from './os_settings_main.html.js';
 
-/**
- * @typedef {{about: boolean, settings: boolean}}
- */
-let MainPageVisibility;
+interface MainPageVisibility {
+  about: boolean;
+  settings: boolean;
+}
 
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {RouteObserverBehaviorInterface}
- */
+interface OsSettingsMainElement {
+  $: {
+    overscroll: HTMLDivElement,
+  };
+}
+
 const OsSettingsMainElementBase =
-    mixinBehaviors([RouteObserverBehavior], PolymerElement);
+    mixinBehaviors([RouteObserverBehavior], PolymerElement) as {
+      new (): PolymerElement & RouteObserverBehaviorInterface,
+    };
 
-/** @polymer */
 class OsSettingsMainElement extends OsSettingsMainElementBase {
   static get is() {
     return 'os-settings-main';
@@ -65,7 +68,6 @@
         notify: true,
       },
 
-      /** @private */
       overscroll_: {
         type: Number,
         observer: 'overscrollChanged_',
@@ -74,7 +76,6 @@
       /**
        * Controls which main pages are displayed via dom-ifs, based on the
        * current route.
-       * @private {!MainPageVisibility}
        */
       showPages_: {
         type: Object,
@@ -83,7 +84,6 @@
         },
       },
 
-      /** @private */
       showingSubpage_: Boolean,
 
       toolbarSpinnerActive: {
@@ -94,7 +94,6 @@
 
       /**
        * Dictionary defining page visibility.
-       * @type {!OSPageVisibility}
        */
       pageVisibility: Object,
 
@@ -114,15 +113,31 @@
     };
   }
 
+  prefs: Object;
+  advancedToggleExpanded: boolean;
+  toolbarSpinnerActive: boolean;
+  pageVisibility: OSPageVisibility;
+  showAndroidApps: boolean;
+  showArcvmManageUsb: boolean;
+  showCrostini: boolean;
+  showReset: boolean;
+  showStartup: boolean;
+  showKerberosSection: boolean;
+  havePlayStoreApp: boolean;
+  private overscroll_: number;
+  private showPages_: MainPageVisibility;
+  private showingSubpage_: boolean;
+  private boundScroll_: (() => void)|null;
+
   constructor() {
     super();
 
-    /** @private {?function(): void} */
     this.boundScroll_ = null;
   }
 
-  /** @private */
-  overscrollChanged_() {
+  private overscrollChanged_() {
+    assert(this.offsetParent);
+
     if (!this.overscroll_ && this.boundScroll_) {
       this.offsetParent.removeEventListener('scroll', this.boundScroll_);
       window.removeEventListener('resize', this.boundScroll_);
@@ -133,6 +148,7 @@
           this.setOverscroll_(0);
         }
       };
+
       this.offsetParent.addEventListener('scroll', this.boundScroll_);
       window.addEventListener('resize', this.boundScroll_);
     }
@@ -141,10 +157,9 @@
   /**
    * Sets the overscroll padding. Never forces a scroll, i.e., always leaves
    * any currently visible overflow as-is.
-   * @param {number=} opt_minHeight The minimum overscroll height needed.
-   * @private
+   * @param minHeight The minimum overscroll height needed.
    */
-  setOverscroll_(opt_minHeight) {
+  private setOverscroll_(minHeight?: number) {
     const scroller = this.offsetParent;
     if (!scroller) {
       return;
@@ -155,16 +170,14 @@
     // How much of the overscroll is visible (may be negative).
     const visibleOverscroll =
         overscroll.scrollHeight - (overscrollBottom - visibleBottom);
-    this.overscroll_ =
-        Math.max(opt_minHeight || 0, Math.ceil(visibleOverscroll));
+    this.overscroll_ = Math.max(minHeight || 0, Math.ceil(visibleOverscroll));
   }
 
   /**
    * Updates the hidden state of the about and settings pages based on the
    * current route.
-   * @param {!Route} newRoute
    */
-  currentRouteChanged(newRoute) {
+  override currentRouteChanged(newRoute: Route) {
     const inAbout =
         routes.ABOUT.contains(Router.getInstance().getCurrentRoute());
     this.showPages_ = {about: inAbout, settings: !inAbout};
@@ -177,13 +190,11 @@
     }
   }
 
-  /** @private */
-  onShowingSubpage_() {
+  private onShowingSubpage_() {
     this.showingSubpage_ = true;
   }
 
-  /** @private */
-  onShowingMainPage_() {
+  private onShowingMainPage_() {
     this.showingSubpage_ = false;
   }
 
@@ -191,29 +202,33 @@
    * A handler for the 'showing-section' event fired from os-settings-page,
    * indicating that a section should be scrolled into view as a result of a
    * navigation.
-   * @param {!CustomEvent<!HTMLElement>} e
-   * @private
    */
-  onShowingSection_(e) {
+  private onShowingSection_(e: CustomEvent<HTMLElement>) {
     const section = e.detail;
     // Calculate the height that the overscroll padding should be set to, so
     // that the given section is displayed at the top of the viewport.
     // Find the distance from the section's top to the overscroll.
-    const sectionTop = section.offsetParent.offsetTop + section.offsetTop;
+    assert(section.offsetParent);
+    const sectionTop =
+        (section.offsetParent as HTMLElement).offsetTop + section.offsetTop;
     const distance = this.$.overscroll.offsetTop - sectionTop;
+
+    assert(this.offsetParent);
     const overscroll = Math.max(0, this.offsetParent.clientHeight - distance);
     this.setOverscroll_(overscroll);
     section.scrollIntoView();
     section.focus();
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  showManagedHeader_() {
+  private showManagedHeader_(): boolean {
     return !this.showingSubpage_ && !this.showPages_.about;
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'os-settings-main': OsSettingsMainElement;
+  }
+}
+
 customElements.define(OsSettingsMainElement.is, OsSettingsMainElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_settings_menu/BUILD.gn
deleted file mode 100644
index ab808f8..0000000
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/BUILD.gn
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-import("../os_settings.gni")
-
-js_type_check("closure_compile_module") {
-  closure_flags = os_settings_closure_flags
-  is_polymer3 = true
-  deps = [ ":os_settings_menu" ]
-}
-
-js_library("os_settings_menu") {
-  deps = [
-    "..:os_route",
-    "..:route_observer_behavior",
-    "../..:router",
-    "//third_party/polymer/v3_0/components-chromium/iron-collapse:iron-collapse",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.ts
similarity index 66%
rename from chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js
rename to chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.ts
index e7e1777..3414ddaa 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.ts
@@ -14,7 +14,10 @@
 import '../../settings_shared.css.js';
 import '../os_icons.js';
 
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {IronCollapseElement} from 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
 import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Route, Router} from '../../router.js';
@@ -23,15 +26,19 @@
 
 import {getTemplate} from './os_settings_menu.html.js';
 
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {RouteObserverBehaviorInterface}
- */
-const OsSettingsMenuElementBase =
-    mixinBehaviors([RouteObserverBehavior], PolymerElement);
+interface OsSettingsMenuElement {
+  $: {
+    topMenu: IronSelectorElement,
+    subMenu: IronSelectorElement,
+    advancedSubmenu: IronCollapseElement,
+  };
+}
 
-/** @polymer */
+const OsSettingsMenuElementBase =
+    mixinBehaviors([RouteObserverBehavior], PolymerElement) as {
+      new (): PolymerElement & RouteObserverBehaviorInterface,
+    };
+
 class OsSettingsMenuElement extends OsSettingsMenuElementBase {
   static get is() {
     return 'os-settings-menu';
@@ -51,7 +58,6 @@
 
       /**
        * Whether the user is in guest mode.
-       * @private{boolean}
        */
       isGuestMode_: {
         type: Boolean,
@@ -61,7 +67,6 @@
 
       /**
        * Whether Accessibility OS Settings visibility improvements are enabled.
-       * @private{boolean}
        */
       isAccessibilityOSSettingsVisibilityEnabled_: {
         type: Boolean,
@@ -82,8 +87,15 @@
     };
   }
 
-  /** @param {!Route} newRoute */
-  currentRouteChanged(newRoute) {
+  advancedOpened: boolean;
+  showCrostini: boolean;
+  showStartup: boolean;
+  showReset: boolean;
+  showKerberosSection: boolean;
+  private isGuestMode_: boolean;
+  private isAccessibilityOSSettingsVisibilityEnabled_: boolean;
+
+  override currentRouteChanged(newRoute: Route) {
     const urlSearchQuery =
         Router.getInstance().getQueryParameters().get('search');
     // If the route navigated to by a search result is in the advanced
@@ -94,10 +106,11 @@
     }
 
     // Focus the initially selected path.
-    const anchors = this.root.querySelectorAll('a');
+    const anchors = this.shadowRoot!.querySelectorAll('a');
     for (let i = 0; i < anchors.length; ++i) {
-      const anchorRoute =
-          Router.getInstance().getRouteForPath(anchors[i].getAttribute('href'));
+      const href = anchors[i].getAttribute('href');
+      assert(href);
+      const anchorRoute = Router.getInstance().getRouteForPath(href);
       if (anchorRoute && anchorRoute.contains(newRoute)) {
         this.setSelectedUrl_(anchors[i].href);
         return;
@@ -107,19 +120,16 @@
     this.setSelectedUrl_('');  // Nothing is selected.
   }
 
-  /** @private */
-  onAdvancedButtonToggle_() {
+  private onAdvancedButtonToggle_() {
     this.advancedOpened = !this.advancedOpened;
   }
 
   /**
    * Prevent clicks on sidebar items from navigating. These are only links for
    * accessibility purposes, taps are handled separately by <iron-selector>.
-   * @param {!Event} event
-   * @private
    */
-  onLinkClick_(event) {
-    if (event.target.matches('a')) {
+  private onLinkClick_(event: Event) {
+    if ((event.target as HTMLElement).matches('a')) {
       event.preventDefault();
     }
   }
@@ -127,43 +137,37 @@
   /**
    * Keeps both menus in sync. |url| needs to come from |element.href| because
    * |iron-list| uses the entire url. Using |getAttribute| will not work.
-   * @param {string} url
    */
-  setSelectedUrl_(url) {
+  private setSelectedUrl_(url: string) {
     this.$.topMenu.selected = this.$.subMenu.selected = url;
   }
 
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  onSelectorActivate_(event) {
+  private onSelectorActivate_(event: CustomEvent<{selected: string}>) {
     this.setSelectedUrl_(event.detail.selected);
   }
 
   /**
-   * @param {boolean} opened Whether the menu is expanded.
-   * @return {string} Which icon to use.
-   * @private
-   * */
-  arrowState_(opened) {
+   * @param opened Whether the menu is expanded.
+   * @return Which icon to use.
+   */
+  private arrowState_(opened: boolean): string {
     return opened ? 'cr:arrow-drop-up' : 'cr:arrow-drop-down';
   }
 
-  /** @return {boolean} Whether the advanced submenu is open. */
-  isAdvancedSubmenuOpenedForTest() {
-    const submenu = /** @type {IronCollapseElement} */ (this.$.advancedSubmenu);
-    return submenu.opened;
+  /** @return Whether the advanced submenu is open. */
+  isAdvancedSubmenuOpenedForTest(): boolean {
+    return this.$.advancedSubmenu.opened;
   }
 
-  /**
-   * @param {boolean} bool
-   * @return {string}
-   * @private
-   */
-  boolToString_(bool) {
+  private boolToString_(bool: boolean): string {
     return bool.toString();
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'os-settings-menu': OsSettingsMenuElement;
+  }
+}
+
 customElements.define(OsSettingsMenuElement.is, OsSettingsMenuElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_settings_ui/BUILD.gn
deleted file mode 100644
index 60f3e8f2..0000000
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/BUILD.gn
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-import("../os_settings.gni")
-
-js_type_check("closure_compile_module") {
-  closure_flags = os_settings_closure_flags
-  is_polymer3 = true
-  deps = [ ":os_settings_ui" ]
-}
-
-js_library("os_settings_ui") {
-  deps = [
-    "..:metrics_recorder",
-    "..:os_page_visibility",
-    "..:os_route",
-    "..:pref_to_setting_metric_converter",
-    "../..:router",
-    "../os_settings_main:os_settings_main",
-    "../os_toolbar:os_toolbar",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements:cr_container_shadow_behavior.m",
-    "//ui/webui/resources/cr_elements:find_shortcut_behavior",
-    "//ui/webui/resources/cr_elements/policy:cr_policy_indicator_behavior.m",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [
-    "../settings_controls_types.js",
-    "//ui/webui/resources/cr_elements/cr_drawer/cr_drawer_externs.js",
-  ]
-}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts
similarity index 68%
rename from chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
rename to chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts
index 757440b..f185c9f 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.ts
@@ -22,48 +22,70 @@
 import '../../prefs/prefs.js';
 import '../../settings_vars.css.js';
 
-import {CrContainerShadowBehavior, CrContainerShadowBehaviorInterface} from 'chrome://resources/cr_elements/cr_container_shadow_behavior.m.js';
-import {FindShortcutBehavior, FindShortcutBehaviorInterface} from 'chrome://resources/cr_elements/find_shortcut_behavior.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
+import {CrContainerShadowBehavior} from 'chrome://resources/cr_elements/cr_container_shadow_behavior.m.js';
+import {CrDrawerElement} from 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
+import {FindShortcutBehavior} from 'chrome://resources/cr_elements/find_shortcut_behavior.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {listenOnce} from 'chrome://resources/js/util.m.js';
-import {Debouncer, microTask, mixinBehaviors, PolymerElement, timeOut} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {Debouncer, DomIf, microTask, mixinBehaviors, PolymerElement, timeOut} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
-import {SettingChangeValue} from '../../mojom-webui/search/user_action_recorder.mojom-webui.js';
-import {Setting} from '../../mojom-webui/setting.mojom-webui.js';
+import {SettingsPrefsElement} from '../../prefs/prefs.js';
 import {Route, Router} from '../../router.js';
 import {setGlobalScrollTarget} from '../global_scroll_target_behavior.js';
 import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSettingChange} from '../metrics_recorder.js';
 import {OSPageVisibility, osPageVisibility} from '../os_page_visibility.js';
+import {OsToolbarElement} from '../os_toolbar/os_toolbar.js';
 import {PrefToSettingMetricConverter} from '../pref_to_setting_metric_converter.js';
 import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js';
 
 import {getTemplate} from './os_settings_ui.html.js';
 
+declare global {
+  interface Window {
+    settings: any;
+    CrPolicyStrings: {[key: string]: string};
+  }
+}
+
+declare global {
+  interface HTMLElementEventMap {
+    'refresh-pref': CustomEvent<string>;
+    'scroll-to-bottom': CustomEvent<{bottom: number, callback: () => void}>;
+    'scroll-to-top': CustomEvent<{top: number, callback: () => void}>;
+    'user-action-setting-change':
+        CustomEvent<{prefKey: string, prefValue: any}>;
+  }
+}
+
 /** Global defined when the main Settings script runs. */
 let defaultResourceLoaded = true;  // eslint-disable-line prefer-const
 
 assert(
     !window.settings || !defaultResourceLoaded,
-    'settings_ui.js run twice. You probably have an invalid import.');
+    'os_settings_ui.js was executed twice. You probably have an invalid import.');
 
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {CrContainerShadowBehaviorInterface}
- * @implements {FindShortcutBehaviorInterface}
- */
-const OsSettingsUiElementBase = mixinBehaviors(
-    [
-      CrContainerShadowBehavior,
-      FindShortcutBehavior,
-      // Calls currentRouteChanged() in attached(),so ensure other behaviors
-      // run their attached() first.
-      RouteObserverBehavior,
-    ],
-    PolymerElement);
+interface OsSettingsUiElement {
+  $: {
+    container: HTMLDivElement,
+    prefs: SettingsPrefsElement,
+  };
+}
 
-/** @polymer */
+const OsSettingsUiElementBase =
+    mixinBehaviors(
+        [
+          CrContainerShadowBehavior,
+          FindShortcutBehavior,
+          // Calls currentRouteChanged() in attached(),so ensure other behaviors
+          // run their attached() first.
+          RouteObserverBehavior,
+        ],
+        PolymerElement) as {
+      new (): PolymerElement & CrContainerShadowBehavior &
+          FindShortcutBehavior & RouteObserverBehaviorInterface,
+    };
+
 class OsSettingsUiElement extends OsSettingsUiElementBase {
   static get is() {
     return 'os-settings-ui';
@@ -80,7 +102,6 @@
        */
       prefs: Object,
 
-      /** @private */
       advancedOpenedInMain_: {
         type: Boolean,
         value: false,
@@ -88,7 +109,6 @@
         observer: 'onAdvancedOpenedInMainChanged_',
       },
 
-      /** @private */
       advancedOpenedInMenu_: {
         type: Boolean,
         value: false,
@@ -96,7 +116,6 @@
         observer: 'onAdvancedOpenedInMenuChanged_',
       },
 
-      /** @private {boolean} */
       toolbarSpinnerActive_: {
         type: Boolean,
         value: false,
@@ -114,45 +133,31 @@
         observer: 'onNarrowChanged_',
       },
 
-      /**
-       * @private {!OSPageVisibility}
-       */
       pageVisibility_: {type: Object, value: osPageVisibility},
 
-      /** @private */
       havePlayStoreApp_: Boolean,
 
-      /** @private */
       showAndroidApps_: Boolean,
 
-      /** @private */
       showArcvmManageUsb_: Boolean,
 
-      /** @private */
       showCrostini_: Boolean,
 
-      /** @private */
       showToolbar_: Boolean,
 
-      /** @private */
       showNavMenu_: Boolean,
 
-      /** @private */
       showPluginVm_: Boolean,
 
-      /** @private */
       showReset_: Boolean,
 
-      /** @private */
       showStartup_: Boolean,
 
-      /** @private */
       showKerberosSection_: Boolean,
 
       /**
        * The threshold at which the toolbar will change from normal to narrow
        * mode, in px.
-       * @private {boolean}
        */
       narrowThreshold_: {
         type: Number,
@@ -161,35 +166,47 @@
     };
   }
 
-  /** @override */
+  prefs: Object;
+  isNarrow: boolean;
+  private advancedOpenedInMain_: boolean;
+  private advancedOpenedInMenu_: boolean;
+  private toolbarSpinnerActive_: boolean;
+  private pageVisibility_: OSPageVisibility;
+  private havePlayStoreApp_: boolean;
+  private showAndroidApps_: boolean;
+  private showArcvmManageUsb_: boolean;
+  private showCrostini_: boolean;
+  private showToolbar_: boolean;
+  private showNavMenu_: boolean;
+  private showPluginVm_: boolean;
+  private showReset_: boolean;
+  private showStartup_: boolean;
+  private showKerberosSection_: boolean;
+  private narrowThreshold_: number;
+  private activeRoute_: Route|null;
+  private prefToSettingMetricConverter_: PrefToSettingMetricConverter;
+  private scrollEndDebouncer_: Debouncer|null;
+
   constructor() {
     super();
 
     /**
      * The route of the selected element in os-settings-menu. Stored here to
      * defer navigation until drawer animation completes.
-     * @private {Route}
      */
     this.activeRoute_ = null;
 
     /**
      * Converts prefs to settings metrics to help record pref changes.
-     * @private {!PrefToSettingMetricConverter}
      */
     this.prefToSettingMetricConverter_ = new PrefToSettingMetricConverter();
 
-    /** @private {?Debouncer} */
     this.scrollEndDebouncer_ = null;
 
     Router.getInstance().initializeRouteFromUrl();
   }
 
-  /**
-   * @override
-   * @suppress {es5Strict} Object literals cannot contain duplicate keys in
-   * ES5 strict mode.
-   */
-  ready() {
+  override ready() {
     super.ready();
 
     window.CrPolicyStrings = {
@@ -236,14 +253,8 @@
       this.$.container.style.visibility = 'hidden';
     });
 
-    this.addEventListener('refresh-pref', (event) => {
-      this.onRefreshPref_(/** @type {!CustomEvent<string>} */ (event));
-    });
-    this.addEventListener('user-action-setting-change', (event) => {
-      this.onSettingChange_(
-          /** @type {!CustomEvent <!{prefKey: string, prefValue: *}>} */ (
-              event));
-    });
+    this.addEventListener('refresh-pref', this.onRefreshPref_);
+    this.addEventListener('user-action-setting-change', this.onSettingChange_);
 
     // If navigation menu is not shown, do not listen to the drawer.
     if (!this.showNavMenu_) {
@@ -253,21 +264,20 @@
     microTask.run(() => {
       // Lazy-create the drawer the first time it is opened or swiped into
       // view.
-      const drawer = /** @type {!CrDrawerElement} */ (
-          this.shadowRoot.querySelector('#drawer'));
-      assert(drawer);
+      const drawer = this.getDrawer_();
       listenOnce(drawer, 'cr-drawer-opening', () => {
-        this.shadowRoot.querySelector('#drawerTemplate').if = true;
+        const drawerTemplate =
+            this.shadowRoot!.querySelector('#drawerTemplate') as DomIf;
+        drawerTemplate.if = true;
       });
 
-      window.addEventListener('popstate', e => {
+      window.addEventListener('popstate', () => {
         drawer.cancel();
       });
     });
   }
 
-  /** @override */
-  connectedCallback() {
+  override connectedCallback() {
     super.connectedCallback();
 
     document.documentElement.classList.remove('loading');
@@ -282,7 +292,7 @@
     document.fonts.load('bold 12px Roboto');
     setGlobalScrollTarget(this.$.container);
 
-    const scrollToTop = top => new Promise(resolve => {
+    const scrollToTop = (top: number) => new Promise<void>(resolve => {
       if (this.$.container.scrollTop === top) {
         resolve();
         return;
@@ -298,13 +308,17 @@
       };
       this.$.container.addEventListener('scroll', onScroll);
     });
-    this.addEventListener('scroll-to-top', e => {
-      scrollToTop(e.detail.top).then(e.detail.callback);
-    });
-    this.addEventListener('scroll-to-bottom', e => {
-      scrollToTop(e.detail.bottom - this.$.container.clientHeight)
-          .then(e.detail.callback);
-    });
+    this.addEventListener(
+        'scroll-to-top',
+        (e: CustomEvent<{top: number, callback: () => void}>) => {
+          scrollToTop(e.detail.top).then(e.detail.callback);
+        });
+    this.addEventListener(
+        'scroll-to-bottom',
+        (e: CustomEvent<{bottom: number, callback: () => void}>) => {
+          scrollToTop(e.detail.bottom - this.$.container.clientHeight)
+              .then(e.detail.callback);
+        });
 
     // Window event listeners will not fire when settings first starts.
     // Blur events before the first focus event do not matter.
@@ -320,8 +334,7 @@
     window.addEventListener('click', recordClick, /*capture=*/ true);
   }
 
-  /** @override */
-  disconnectedCallback() {
+  override disconnectedCallback() {
     super.disconnectedCallback();
 
     window.removeEventListener('focus', recordPageFocus);
@@ -330,11 +343,7 @@
     Router.getInstance().resetRouteForTesting();
   }
 
-  /**
-   * @param {!Route} newRoute
-   * @param {!Route=} oldRoute
-   */
-  currentRouteChanged(newRoute, oldRoute) {
+  override currentRouteChanged(newRoute: Route, oldRoute?: Route) {
     if (oldRoute && newRoute !== oldRoute) {
       // Search triggers route changes and currentRouteChanged() is called
       // in attached() state which is extraneous for this metric.
@@ -352,41 +361,42 @@
   }
 
   // Override FindShortcutBehavior methods.
-  handleFindShortcut(modalContextOpen) {
+  override handleFindShortcut(modalContextOpen: boolean) {
     if (modalContextOpen || !this.showToolbar_) {
       return false;
     }
-    this.shadowRoot.querySelector('os-toolbar').getSearchField().showAndFocus();
-    this.shadowRoot.querySelector('os-toolbar')
-        .getSearchField()
-        .getSearchInput()
-        .select();
+    const toolbar = this.getToolbar_();
+    toolbar.getSearchField().showAndFocus();
+    toolbar.getSearchField().getSearchInput().select();
     return true;
   }
 
   // Override FindShortcutBehavior methods.
-  searchInputHasFocus() {
+  override searchInputHasFocus() {
     if (!this.showToolbar_) {
       return false;
     }
-    return this.shadowRoot.querySelector('os-toolbar')
-        .getSearchField()
-        .isSearchFocused();
+
+    return this.getToolbar_().getSearchField().isSearchFocused();
   }
 
-  /**
-   * @param {!CustomEvent<string>} e
-   * @private
-   */
-  onRefreshPref_(e) {
-    return /** @type {SettingsPrefsElement} */ (this.$.prefs).refresh(e.detail);
+  private getDrawer_(): CrDrawerElement {
+    const drawer = this.shadowRoot!.querySelector('#drawer');
+    assert(drawer);
+    return drawer as CrDrawerElement;
   }
 
-  /**
-   * @param {!CustomEvent<!{prefKey: string, prefValue: *}>} e
-   * @private
-   */
-  onSettingChange_(e) {
+  private getToolbar_(): OsToolbarElement {
+    const toolbar = this.shadowRoot!.querySelector('os-toolbar');
+    assert(toolbar);
+    return toolbar;
+  }
+
+  private onRefreshPref_(e: CustomEvent<string>) {
+    return this.$.prefs.refresh(e.detail);
+  }
+
+  private onSettingChange_(e: CustomEvent<{prefKey: string, prefValue: any}>) {
     const {prefKey, prefValue} = e.detail;
     const settingMetric =
         this.prefToSettingMetricConverter_.convertPrefToSettingMetric(
@@ -403,10 +413,8 @@
 
   /**
    * Called when a section is selected.
-   * @param {!Event} e
-   * @private
    */
-  onIronActivate_(e) {
+  private onIronActivate_(e: CustomEvent<{selected: string}>) {
     assert(this.showNavMenu_);
     const section = e.detail.selected;
     const path = new URL(section).pathname;
@@ -417,29 +425,27 @@
     if (this.isNarrow) {
       // If the onIronActivate event came from the drawer, close the drawer
       // and wait for the menu to close before navigating to |activeRoute_|.
-      this.shadowRoot.querySelector('#drawer').close();
+      this.getDrawer_().close();
       return;
     }
     this.navigateToActiveRoute_();
   }
 
-  /** @private */
-  onMenuButtonTap_() {
+  private onMenuButtonTap_() {
     if (!this.showNavMenu_) {
       return;
     }
-    this.shadowRoot.querySelector('#drawer').toggle();
+    this.getDrawer_().toggle();
   }
 
   /**
    * Navigates to |activeRoute_| if set. Used to delay navigation until after
    * animations complete to ensure focus ends up in the right place.
-   * @private
    */
-  navigateToActiveRoute_() {
+  private navigateToActiveRoute_() {
     if (this.activeRoute_) {
       Router.getInstance().navigateTo(
-          this.activeRoute_, /* dynamicParams */ null,
+          this.activeRoute_, /* dynamicParams */ undefined,
           /* removeSearch */ true);
       this.activeRoute_ = null;
     }
@@ -452,10 +458,9 @@
    * main settings container is given focus. That way the arrow keys can be
    * used to scroll the container, and pressing tab focuses a component in
    * settings.
-   * @private
    */
-  onMenuClose_() {
-    if (!this.shadowRoot.querySelector('#drawer').wasCanceled()) {
+  private onMenuClose_() {
+    if (!this.getDrawer_().wasCanceled()) {
       // If a navigation happened, MainPageBehavior#currentRouteChanged
       // handles focusing the corresponding section when we call
       // settings.NavigateTo().
@@ -472,36 +477,40 @@
     });
   }
 
-  /** @private */
-  onAdvancedOpenedInMainChanged_() {
+  private onAdvancedOpenedInMainChanged_() {
     // Only sync value when opening, not closing.
     if (this.advancedOpenedInMain_) {
       this.advancedOpenedInMenu_ = true;
     }
   }
 
-  /** @private */
-  onAdvancedOpenedInMenuChanged_() {
+  private onAdvancedOpenedInMenuChanged_() {
     // Only sync value when opening, not closing.
     if (this.advancedOpenedInMenu_) {
       this.advancedOpenedInMain_ = true;
     }
   }
 
-  /** @private */
-  onNarrowChanged_() {
-    if (this.showNavMenu_ && this.shadowRoot.querySelector('#drawer').open &&
-        !this.isNarrow) {
-      this.shadowRoot.querySelector('#drawer').close();
+  private onNarrowChanged_() {
+    if (this.showNavMenu_) {
+      const drawer = this.getDrawer_();
+      if (drawer.open && !this.isNarrow) {
+        drawer.close();
+      }
     }
   }
 
   /**
    * Handles a tap on the drawer's icon.
-   * @private
    */
-  onDrawerIconClick_() {
-    this.shadowRoot.querySelector('#drawer').cancel();
+  private onDrawerIconClick_() {
+    this.getDrawer_().cancel();
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'os-settings-ui': OsSettingsUiElement;
   }
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn
deleted file mode 100644
index 90205371..0000000
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/BUILD.gn
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-import("../os_settings.gni")
-
-js_type_check("closure_compile_module") {
-  closure_flags = os_settings_closure_flags
-  is_polymer3 = true
-  deps = [ ":os_toolbar" ]
-}
-
-js_library("os_toolbar") {
-  deps = [
-    "../os_settings_search_box:os_settings_search_box",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-  externs_list = [ "//ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field_externs.js" ]
-}
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.ts
similarity index 72%
rename from chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
rename to chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.ts
index 634087c..88fb5cc 100644
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
+++ b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.ts
@@ -8,15 +8,16 @@
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/cr_elements/shared_vars_css.m.js';
 import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
-import '../os_settings_search_box/os_settings_search_box.js';
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
+import '../os_settings_search_box/os_settings_search_box.js';
 
+import {CrToolbarSearchFieldElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './os_toolbar.html.js';
 
-/** @polymer */
-class OsToolbarElement extends PolymerElement {
+export class OsToolbarElement extends PolymerElement {
   static get is() {
     return 'os-toolbar';
   }
@@ -48,14 +49,12 @@
        * True when the toolbar is displaying in an extremely narrow mode that
        * the viewport may cutoff an OsSettingsSearchBox with a specific px
        * width.
-       * @private
        */
       isSearchBoxCutoff_: {
         type: Boolean,
         reflectToAttribute: true,
       },
 
-      /** @private */
       showingSearch_: {
         type: Boolean,
         reflectToAttribute: true,
@@ -63,19 +62,33 @@
     };
   }
 
-  /** @return {?CrToolbarSearchFieldElement} */
-  getSearchField() {
-    return /** @type {?CrToolbarSearchFieldElement} */ (
-        this.shadowRoot.querySelector('os-settings-search-box')
-            .shadowRoot.querySelector('cr-toolbar-search-field'));
+  spinnerActive: boolean;
+  showMenu: boolean;
+  showSearch: boolean;
+  narrow: boolean;
+  private isSearchBoxCutoff_: boolean;
+  private showingSearch_: boolean;
+
+  getSearchField(): CrToolbarSearchFieldElement {
+    const searchBox = this.shadowRoot!.querySelector('os-settings-search-box');
+    assert(searchBox);
+    const searchField =
+        searchBox.shadowRoot!.querySelector('cr-toolbar-search-field');
+    assert(searchField);
+    return searchField;
   }
 
-  /** @private */
-  onMenuTap_() {
+  private onMenuTap_() {
     const event =
         new CustomEvent('os-toolbar-menu-tap', {bubbles: true, composed: true});
     this.dispatchEvent(event);
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'os-toolbar': OsToolbarElement;
+  }
+}
+
 customElements.define(OsToolbarElement.is, OsToolbarElement);
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.html b/chrome/browser/resources/settings/privacy_page/cookies_page.html
index 4830f1a..f97c322 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.html
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.html
@@ -108,6 +108,18 @@
               <iron-icon icon="settings:block"></iron-icon>
               <div class="secondary">$i18n{cookiePageBlockThirdBulTwo}</div>
             </div>
+            <template is="dom-if" if="[[enableFirstPartySetsUI_]]">
+              <settings-toggle-button
+                  id="firstPartySetsToggle"
+                  pref="{{
+                      prefs.privacy_sandbox.first_party_sets_data_access_allowed
+                      }}"
+                  label="$i18n{cookiePageFpsLabel}"
+                  sub-label="$i18n{cookiePageFpsSubLabel}"
+                  disabled="[[firstPartySetsToggleDisabled_(
+                      prefs.generated.cookie_primary_setting.value)]]">
+              </settings-toggle-button>
+            </template>
           </div>
         </settings-collapse-radio-button>
         <settings-collapse-radio-button id="blockAll"
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.ts b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
index 2376d68..455d36e 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
@@ -150,6 +150,11 @@
         type: Object,
         observer: 'focusConfigChanged_',
       },
+
+      enableFirstPartySetsUI_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('firstPartySetsUIEnabled'),
+      },
     };
   }
 
@@ -316,6 +321,11 @@
     this.shadowRoot!.querySelector<HTMLAnchorElement>(
                         '#privacySandboxLink')!.click();
   }
+
+  private firstPartySetsToggleDisabled_() {
+    return this.getPref('generated.cookie_primary_setting').value !==
+        CookiePrimarySetting.BLOCK_THIRD_PARTY;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 7e5dc12d..046f0ce 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -1437,10 +1437,7 @@
       false /* expect cert status error */);
 }
 
-class SignedExchangeSecurityStateTest
-    : public CertVerifierBrowserTest,
-      public testing::WithParamInterface<
-          bool /* sxg_subresource_prefetch_enabled */> {
+class SignedExchangeSecurityStateTest : public CertVerifierBrowserTest {
  public:
   SignedExchangeSecurityStateTest() = default;
   ~SignedExchangeSecurityStateTest() override = default;
@@ -1459,16 +1456,6 @@
 
  private:
   void SetUp() override {
-    const bool sxg_subresource_prefetch_enabled = GetParam();
-    std::vector<base::Feature> enabled_features;
-    std::vector<base::Feature> disabled_features;
-    if (sxg_subresource_prefetch_enabled) {
-      enabled_features.push_back(features::kSignedExchangeSubresourcePrefetch);
-    } else {
-      disabled_features.push_back(features::kSignedExchangeSubresourcePrefetch);
-    }
-    feature_list_.InitWithFeatures(enabled_features, disabled_features);
-
     sxg_test_helper_.SetUp();
 
     CertVerifierBrowserTest::SetUp();
@@ -1480,10 +1467,9 @@
   }
 
   content::SignedExchangeBrowserTestHelper sxg_test_helper_;
-  base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(SignedExchangeSecurityStateTest, SecurityLevelIsSecure) {
+IN_PROC_BROWSER_TEST_F(SignedExchangeSecurityStateTest, SecurityLevelIsSecure) {
   embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1508,7 +1494,7 @@
       false /* expect_ran_mixed_content */, false /* expect_cert_error */);
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangeSecurityStateTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangeSecurityStateTest,
                        SecurityLevelIsSecureAfterPrefetch) {
   embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -1521,9 +1507,7 @@
       embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");
 
   // prefetch.html prefetches the signed exchange. And the signed exchange will
-  // be served from HTTPCache (when SignedExchangePrefetchCacheForNavigations is
-  // not enabled), or from PrefetchedSignedExchangeCache (when
-  // SignedExchangePrefetchCacheForNavigations is enabled).
+  // be served from PrefetchedSignedExchangeCache.
   const GURL prefetch_html_url = embedded_test_server()->GetURL(
       std::string("/sxg/prefetch.html#") + sxg_url.spec());
   {
@@ -1554,8 +1538,6 @@
       false /* expect_ran_mixed_content */, false /* expect_cert_error */);
 }
 
-INSTANTIATE_TEST_SUITE_P(, SignedExchangeSecurityStateTest, testing::Bool());
-
 class SecurityStateTabHelperPrerenderTest : public SecurityStateTabHelperTest {
  public:
   SecurityStateTabHelperPrerenderTest()
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
index 87987da..6e6986d0 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
 #include "chrome/android/features/keyboard_accessory/jni_headers/ManualFillingComponentBridge_jni.h"
 #include "chrome/android/features/keyboard_accessory/jni_headers/UserInfoField_jni.h"
 #include "chrome/browser/autofill/manual_filling_controller.h"
@@ -60,11 +61,13 @@
 
 void ManualFillingViewAndroid::OnItemsAvailable(
     const AccessorySheetData& data) {
+  TRACE_EVENT_BEGIN0("passwords", __func__);
   if (auto obj = GetOrCreateJavaObject()) {
     JNIEnv* env = base::android::AttachCurrentThread();
     Java_ManualFillingComponentBridge_onItemsAvailable(
         env, obj, ConvertAccessorySheetDataToJavaObject(env, data));
   }
+  TRACE_EVENT_END0("passwords", __func__);
 }
 
 void ManualFillingViewAndroid::CloseAccessorySheet() {
@@ -82,10 +85,12 @@
 }
 
 void ManualFillingViewAndroid::ShowWhenKeyboardIsVisible() {
+  TRACE_EVENT_BEGIN0("passwords", __func__);
   if (auto obj = GetOrCreateJavaObject()) {
     Java_ManualFillingComponentBridge_showWhenKeyboardIsVisible(
         base::android::AttachCurrentThread(), obj);
   }
+  TRACE_EVENT_END0("passwords", __func__);
 }
 
 void ManualFillingViewAndroid::Hide() {
@@ -162,6 +167,7 @@
 ManualFillingViewAndroid::ConvertAccessorySheetDataToJavaObject(
     JNIEnv* env,
     const AccessorySheetData& tab_data) {
+  TRACE_EVENT_BEGIN0("passwords", __func__);
   DCHECK(java_object_internal_);
   ScopedJavaLocalRef<jobject> j_tab_data =
       Java_ManualFillingComponentBridge_createAccessorySheetData(
@@ -217,6 +223,7 @@
         static_cast<int>(footer_command.accessory_action()));
   }
   return j_tab_data;
+  TRACE_EVENT_END0("passwords", __func__);
 }
 
 AccessorySheetField ManualFillingViewAndroid::ConvertJavaUserInfoField(
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index b14c78b..ebca99b 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -74,10 +74,7 @@
 // duplicates of these results for the suggestion chips.
 constexpr size_t kMaxZeroStateFileResults = 20;
 constexpr size_t kMaxZeroStateDriveResults = 10;
-
-// TODO(warx): Need UX spec.
 constexpr size_t kMaxAppShortcutResults = 4;
-
 constexpr size_t kMaxPlayStoreResults = 12;
 constexpr size_t kMaxAssistantTextResults = 1;
 
diff --git a/chrome/browser/ui/browser_close_unittest.cc b/chrome/browser/ui/browser_close_unittest.cc
index cd4b85a2..d84e0c1 100644
--- a/chrome/browser/ui/browser_close_unittest.cc
+++ b/chrome/browser/ui/browser_close_unittest.cc
@@ -51,6 +51,11 @@
     return nullptr;
   }
 
+  DownloadUIController* GetDownloadUIController() override {
+    ADD_FAILURE();
+    return nullptr;
+  }
+
   DownloadHistory* GetDownloadHistory() override {
     ADD_FAILURE();
     return nullptr;
diff --git a/chrome/browser/ui/tabs/tab_group.cc b/chrome/browser/ui/tabs/tab_group.cc
index 6f28002..412e93a 100644
--- a/chrome/browser/ui/tabs/tab_group.cc
+++ b/chrome/browser/ui/tabs/tab_group.cc
@@ -95,6 +95,9 @@
 }
 
 bool TabGroup::IsSaved() const {
+  // TODO(dljames): Retrieving the service factory each time we want to check
+  // the saved status of a tab group is expensive computationally. Find a way to
+  // simplify this.
   SavedTabGroupKeyedService* backend =
       SavedTabGroupServiceFactory::GetForProfile(controller_->GetProfile());
   return backend && backend->model() && backend->model()->Contains(id());
diff --git a/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc b/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc
index 0a445c3a..10da8a2 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_browsertest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/passwords/manage_passwords_test.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/passwords/password_auto_sign_in_view.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "content/public/test/browser_test.h"
 #include "ui/views/test/ax_event_counter.h"
 
@@ -21,25 +20,15 @@
 
 // Test params:
 //  - bool : when true, the test is setup for users that sync their passwords.
-//  - bool : when true, the unified password manager branding feature is
-//  enabled.
 class PasswordBubbleBrowserTest
     : public SupportsTestDialog<ManagePasswordsTest>,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
+      public testing::WithParamInterface<bool> {
  public:
-  PasswordBubbleBrowserTest() {
-    if (std::get<1>(GetParam())) {
-      scoped_feature_list_.InitAndEnableFeature(
-          password_manager::features::kUnifiedPasswordManagerDesktop);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          password_manager::features::kUnifiedPasswordManagerDesktop);
-    }
-  }
+  PasswordBubbleBrowserTest() = default;
   ~PasswordBubbleBrowserTest() override = default;
 
   void ShowUi(const std::string& name) override {
-    ConfigurePasswordSync(std::get<0>(GetParam()));
+    ConfigurePasswordSync(GetParam());
     if (StartsWith(name, "PendingPasswordBubble",
                    base::CompareCase::SENSITIVE)) {
       SetupPendingPassword();
@@ -77,9 +66,6 @@
       return;
     }
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(PasswordBubbleBrowserTest,
@@ -112,7 +98,7 @@
 IN_PROC_BROWSER_TEST_P(PasswordBubbleBrowserTest,
                        InvokeUi_MoveToAccountStoreBubble) {
   // This test isn't relevant for sync'ing users.
-  if (std::get<0>(GetParam()))
+  if (GetParam())
     return;
   ShowAndVerifyUi();
 }
@@ -127,6 +113,4 @@
   EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kAlert));
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         PasswordBubbleBrowserTest,
-                         testing::Combine(testing::Bool(), testing::Bool()));
+INSTANTIATE_TEST_SUITE_P(All, PasswordBubbleBrowserTest, testing::Bool());
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index f89dae8e..8bbeb54 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -156,7 +156,8 @@
   DCHECK(update_engine_client->GetLastStatus().current_operation() ==
          update_engine::Operation::UPDATED_BUT_DEFERRED);
 
-  update_engine_client->ApplyDeferredUpdate(base::DoNothing());
+  update_engine_client->ApplyDeferredUpdate(/*shutdown_after_update=*/false,
+                                            base::DoNothing());
 }
 
 void VersionUpdaterCros::CheckForUpdate(StatusCallback callback,
diff --git a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
index ffa12ba..4b0a803 100644
--- a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
+++ b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -26,6 +26,8 @@
       IDR_PASSWORD_MANAGER_PASSWORD_MANAGER_HTML);
 
   static constexpr webui::LocalizedString kStrings[] = {
+      {"clearSearch", IDS_CLEAR_SEARCH},
+      {"searchPrompt", IDS_PASSWORD_MANAGER_UI_SEARCH_PROMPT},
       {"title", IDS_PASSWORD_MANAGER_UI_TITLE},
   };
   source->AddLocalizedStrings(kStrings);
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
index c2dc469..971fee3 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -177,8 +177,8 @@
         base::BindRepeating(&CrostiniHandler::HandleStopContainer,
                             handler_weak_ptr_factory_.GetWeakPtr()));
     web_ui()->RegisterMessageCallback(
-        "applyAnsiblePlaybook",
-        base::BindRepeating(&CrostiniHandler::HandleApplyAnsiblePlaybook,
+        "openContainerFileSelector",
+        base::BindRepeating(&CrostiniHandler::HandleOpenContainerFileSelector,
                             handler_weak_ptr_factory_.GetWeakPtr()));
   }
 }
@@ -812,20 +812,19 @@
   }
 }
 
-void CrostiniHandler::HandleApplyAnsiblePlaybook(
+void CrostiniHandler::HandleOpenContainerFileSelector(
     const base::Value::List& args) {
   CHECK_EQ(1U, args.size());
   const std::string& callback_id = args[0].GetString();
-  ansible_file_selector_ =
-      std::make_unique<crostini::AnsibleFileSelector>(web_ui());
-  ansible_file_selector_->SelectFile(
-      base::BindOnce(&CrostiniHandler::OnAnsiblePlaybookSelected,
+  file_selector_ = std::make_unique<crostini::CrostiniFileSelector>(web_ui());
+  file_selector_->SelectFile(
+      base::BindOnce(&CrostiniHandler::OnContainerFileSelected,
                      handler_weak_ptr_factory_.GetWeakPtr(), callback_id),
       base::DoNothing());
 }
 
-void CrostiniHandler::OnAnsiblePlaybookSelected(const std::string& callback_id,
-                                                const base::FilePath& path) {
+void CrostiniHandler::OnContainerFileSelected(const std::string& callback_id,
+                                              const base::FilePath& path) {
   base::Value filePath(path.value());
   ResolveJavascriptCallback(base::Value(callback_id), filePath);
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
index 101c11fb..4658b61 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h
@@ -8,8 +8,8 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ash/crostini/ansible/ansible_file_selector.h"
 #include "chrome/browser/ash/crostini/crostini_export_import.h"
+#include "chrome/browser/ash/crostini/crostini_file_selector.h"
 #include "chrome/browser/ash/crostini/crostini_manager.h"
 #include "chrome/browser/ash/crostini/crostini_port_forwarder.h"
 #include "chrome/browser/ash/guest_os/guest_id.h"
@@ -145,16 +145,16 @@
   // Handle a request to stop a running lxd container
   void HandleStopContainer(const base::Value::List& args);
 
-  // Handle a request to upload an Ansible Playbook
-  void HandleApplyAnsiblePlaybook(const base::Value::List& args);
-  // Callback for AnsibleFileSelector
-  void OnAnsiblePlaybookSelected(const std::string& callback_id,
-                                 const base::FilePath& path);
+  // Handle a request to open a file selector
+  void HandleOpenContainerFileSelector(const base::Value::List& args);
+  // Callback for CrostiniFileSelector
+  void OnContainerFileSelected(const std::string& callback_id,
+                               const base::FilePath& path);
 
   Profile* profile_;
   base::CallbackListSubscription adb_sideloading_device_policy_subscription_;
   PrefChangeRegistrar pref_change_registrar_;
-  std::unique_ptr<crostini::AnsibleFileSelector> ansible_file_selector_;
+  std::unique_ptr<crostini::CrostiniFileSelector> file_selector_;
 
   // |handler_weak_ptr_factory_| is used for callbacks handling messages from
   // the WebUI page, and certain observers. These callbacks usually have the
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index aa96357..739ef7e3 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1827,6 +1827,10 @@
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_PRIVACY_SANDBOX_AD_MEASUREMENT_DIALOG_CONTROL_MEASUREMENT,
           base::ASCIIToUTF16(chrome::kChromeUIHistoryURL)));
+  html_source->AddBoolean(
+      "firstPartySetsUIEnabled",
+      base::FeatureList::IsEnabled(
+          privacy_sandbox::kPrivacySandboxFirstPartySetsUI));
 }
 
 void AddPrivacyGuideStrings(content::WebUIDataSource* html_source) {
@@ -2169,6 +2173,9 @@
     {"cookiePageBlockAllBulOne", IDS_SETTINGS_COOKIES_BLOCK_ALL_BULLET_ONE},
     {"cookiePageBlockAllBulTwo", IDS_SETTINGS_COOKIES_BLOCK_ALL_BULLET_TWO},
     {"cookiePageBlockAllBulThree", IDS_SETTINGS_COOKIES_BLOCK_ALL_BULLET_THREE},
+    {"cookiePageFpsLabel", IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_LABEL},
+    {"cookiePageFpsSubLabel",
+     IDS_SETTINGS_COOKIES_FIRST_PARTY_SETS_TOGGLE_SUB_LABEL},
     {"cookiePageClearOnExit", IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT},
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
     {"cookiePageClearOnExitDesc", IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT_DESC},
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 7ff8b0b1..0245cd13 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1660845461-6907d5caac9a3a7552d52bf333c970cd7960238a.profdata
+chrome-linux-main-1660879972-650f9070d0512d3d5e49b02840ec47ecc0a04b5d.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 80907e9d..deed3db 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1660845461-6d2b87a64ef678c60f5022188c9f845300d5c23d.profdata
+chrome-mac-arm-main-1660879972-910c94f0b1c50563b112624e60d83e4e5eddbc1d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index f44b3cd..e8a005c8 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1660845461-3452705c6146cc61ae9f7b278a773834dc6bec58.profdata
+chrome-mac-main-1660879972-60837d0b461a1b60719a1e9b9402cbe60ad6f3b1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5aee8215..ab01a248 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1660845461-8a929a60e3c06ed3ad0ff274f1378b08fca86633.profdata
+chrome-win32-main-1660879972-e0641a0e6e1cc7d32855daec3cdf17074ecaf2c3.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5ef5d0a..26073b6 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1660856251-a75289f573d32a47d93bd9871727118279558c67.profdata
+chrome-win64-main-1660879972-4e5365809420c5df82209082bb58f6f5ebf78a26.profdata
diff --git a/chrome/common/profiler/main_thread_stack_sampling_profiler.cc b/chrome/common/profiler/main_thread_stack_sampling_profiler.cc
index 14e7834..36a38f6 100644
--- a/chrome/common/profiler/main_thread_stack_sampling_profiler.cc
+++ b/chrome/common/profiler/main_thread_stack_sampling_profiler.cc
@@ -47,11 +47,11 @@
 // those users.
 #if defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
   if (process == metrics::CallStackProfileParams::Process::kBrowser &&
-      !UnwindPrerequisites::Available()) {
+      !AreUnwindPrerequisitesAvailable()) {
     const version_info::Channel channel = chrome::GetChannel();
     if (channel == version_info::Channel::CANARY ||
         channel == version_info::Channel::DEV) {
-      UnwindPrerequisites::RequestInstallation();
+      RequestUnwindPrerequisitesInstallation();
     }
   }
 #endif
diff --git a/chrome/common/profiler/thread_profiler_configuration.cc b/chrome/common/profiler/thread_profiler_configuration.cc
index ac0cb48..d06c4f33 100644
--- a/chrome/common/profiler/thread_profiler_configuration.cc
+++ b/chrome/common/profiler/thread_profiler_configuration.cc
@@ -198,7 +198,7 @@
   if (!platform_configuration.IsSupported(release_channel))
     return absl::nullopt;
 
-  if (!UnwindPrerequisites::Available()) {
+  if (!AreUnwindPrerequisitesAvailable()) {
     return kProfileDisabledModuleNotInstalled;
   }
 
diff --git a/chrome/common/profiler/unwind_util.cc b/chrome/common/profiler/unwind_util.cc
index 1ac6098c..31e1c42 100644
--- a/chrome/common/profiler/unwind_util.cc
+++ b/chrome/common/profiler/unwind_util.cc
@@ -144,8 +144,7 @@
 
 }  // namespace
 
-// static
-void UnwindPrerequisites::RequestInstallation() {
+void RequestUnwindPrerequisitesInstallation() {
   CHECK_EQ(metrics::CallStackProfileParams::Process::kBrowser,
            GetProfileParamsProcess(*base::CommandLine::ForCurrentProcess()));
 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARMEL)
@@ -155,8 +154,7 @@
 #endif  // BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARMEL)
 }
 
-// static
-bool UnwindPrerequisites::Available() {
+bool AreUnwindPrerequisitesAvailable() {
 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARMEL)
   return stack_unwinder::Module::IsInstalled();
 #else
@@ -165,7 +163,7 @@
 }
 
 base::StackSamplingProfiler::UnwindersFactory CreateCoreUnwindersFactory() {
-  if (!UnwindPrerequisites::Available()) {
+  if (!AreUnwindPrerequisitesAvailable()) {
     return base::StackSamplingProfiler::UnwindersFactory();
   }
 #if BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
diff --git a/chrome/common/profiler/unwind_util.h b/chrome/common/profiler/unwind_util.h
index d8b6c788..3c2b48a 100644
--- a/chrome/common/profiler/unwind_util.h
+++ b/chrome/common/profiler/unwind_util.h
@@ -7,24 +7,22 @@
 
 #include "base/profiler/stack_sampling_profiler.h"
 
-// A helper class to encapsulate some functionality related to stack unwinding.
-class UnwindPrerequisites {
- public:
-  // Request the installation of any prerequisites needed for unwinding.
-  // Android, in particular, requires use of a dynamic feature module to provide
-  // the native unwinder.
-  //
-  // Note that installation of some prerequisites can occur asynchronously.
-  // Therefore, it's not guaranteed that Available() will return true
-  // immediately after calling RequestInstallation().
-  //
-  // RequestInstallation() can only be called from the browser process.
-  static void RequestInstallation();
+// Request the installation of any prerequisites needed for unwinding.
+// Android, in particular, requires use of a dynamic feature module to provide
+// the native unwinder.
+//
+// Note that installation of some prerequisites can occur asynchronously.
+// Therefore, it's not guaranteed that AreUnwindPrerequisitesAvailable() will
+// return true immediately after calling
+// RequestUnwindPrerequisitesInstallation().
+//
+// RequestUnwindPrerequisitesInstallation() can only be called from the browser
+// process.
+void RequestUnwindPrerequisitesInstallation();
 
-  // Are the prerequisites required for unwinding available in the current
-  // context?
-  static bool Available();
-};
+// Are the prerequisites required for unwinding available in the current
+// context?
+bool AreUnwindPrerequisitesAvailable();
 
 base::StackSamplingProfiler::UnwindersFactory CreateCoreUnwindersFactory();
 
diff --git a/chrome/common/profiler/unwind_util_unittest.cc b/chrome/common/profiler/unwind_util_unittest.cc
index b6c9614..d38b95d 100644
--- a/chrome/common/profiler/unwind_util_unittest.cc
+++ b/chrome/common/profiler/unwind_util_unittest.cc
@@ -12,12 +12,12 @@
 TEST(UnwindPrerequisitesDeathTest, CannotInstallOutsideBrowser) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kProcessType, switches::kRendererProcess);
-  ASSERT_DEATH_IF_SUPPORTED(UnwindPrerequisites::RequestInstallation(), "");
+  ASSERT_DEATH_IF_SUPPORTED(RequestUnwindPrerequisitesInstallation(), "");
 }
 
 TEST(UnwindPrerequisitesTest, CanInstallInsideBrowser) {
   // No process type switch implies browser process.
   *base::CommandLine::ForCurrentProcess() =
       base::CommandLine(base::CommandLine::NO_PROGRAM);
-  UnwindPrerequisites::RequestInstallation();
+  RequestUnwindPrerequisitesInstallation();
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ade95f35..a8643dd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8816,6 +8816,7 @@
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
     ]
   }
diff --git a/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.ts b/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.ts
index 41efd53..58c8c1b 100644
--- a/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.ts
+++ b/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.ts
@@ -37,14 +37,16 @@
     const input = /** @type {!CrInputElement} */ (
         dropDown.shadowRoot!.querySelector('cr-input')!);
     input.value = searchTerm;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     flush();
   }
 
   function blur() {
     const input = /** @type {!CrInputElement} */ (
         dropDown.shadowRoot!.querySelector('cr-input')!);
-    input.fire('blur');
+    input.dispatchEvent(
+        new CustomEvent('blur', {bubbles: true, composed: true}));
     flush();
   }
 
diff --git a/chrome/test/data/webui/cr_elements/cr_toggle_test.ts b/chrome/test/data/webui/cr_elements/cr_toggle_test.ts
index c6de618..81bd63e 100644
--- a/chrome/test/data/webui/cr_elements/cr_toggle_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toggle_test.ts
@@ -25,8 +25,7 @@
     assertTrue(toggle.hasAttribute('checked'));
     assertEquals('true', toggle.getAttribute('aria-pressed'));
     // Asserting that the toggle button has actually moved.
-    assertTrue(
-        getComputedStyle(toggle.$$('#knob')!).transform.includes('matrix'));
+    assertTrue(getComputedStyle(toggle.$.knob).transform.includes('matrix'));
   }
 
   function assertNotChecked() {
@@ -34,7 +33,7 @@
     assertEquals(null, toggle.getAttribute('checked'));
     assertEquals('false', toggle.getAttribute('aria-pressed'));
     // Asserting that the toggle button has not moved.
-    assertEquals('none', getComputedStyle(toggle.$$('#knob')!).transform);
+    assertEquals('none', getComputedStyle(toggle.$.knob).transform);
   }
 
   function assertDisabled() {
diff --git a/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts b/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
index 40ad33b..d11ebbe 100644
--- a/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
+++ b/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
@@ -259,7 +259,8 @@
     const input = dialog.shadowRoot!.querySelector('cr-input');
     assertTrue(!!input);
     input.value = 'https://example.com';
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
 
     // Closing the dialog (as opposed to canceling) should keep the
     // selectHostAccess value at ON_SPECIFIC_SITES.
diff --git a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
index 2dc6481..8ae0195 100644
--- a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
@@ -53,7 +53,8 @@
     assertTrue(!!input);
     const site = 'http://www.example.com';
     input.value = site;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
 
     const submit = dialog.$.submit;
@@ -76,13 +77,15 @@
     // Simulate user input of invalid text.
     const invalidSite = 'foobar';
     input.value = invalidSite;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertTrue(input.invalid);
     assertTrue(submit.disabled);
 
     // Entering valid text should clear the error and enable the submit button.
     input.value = 'http://www.example.com';
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
     assertFalse(submit.disabled);
   });
@@ -94,7 +97,8 @@
     assertTrue(!!input);
     const site = 'http://....a';
     input.value = site;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
 
     const submit = dialog.$.submit;
@@ -113,7 +117,8 @@
     const input = dialog.shadowRoot!.querySelector('cr-input');
     assertTrue(!!input);
     input.value = newPattern;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     const submit = dialog.$.submit;
 
     submit.click();
@@ -158,7 +163,8 @@
     assertTrue(!!input);
     const site = 'http://www.example.com';
     input.value = site;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
 
     const submit = dialog.$.submit;
@@ -229,13 +235,15 @@
     const input = dialog.shadowRoot!.querySelector('cr-input');
     assertTrue(!!input);
     input.value = 'http://www.nomatch.com';
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
     assertFalse(isVisible(dialog.shadowRoot!.querySelector(
         '.matching-restricted-sites-warning')));
 
     input.value = 'http://*.restricted.com';
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
     assertTrue(isVisible(dialog.shadowRoot!.querySelector(
         '.matching-restricted-sites-warning')));
diff --git a/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts b/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
index 39c0fab..d139238 100644
--- a/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/site_permissions_edit_url_dialog_test.ts
@@ -30,7 +30,8 @@
     assertTrue(!!input);
     const site = 'http://www.example.com';
     input.value = site;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
 
     const submit = element.$.submit;
@@ -54,19 +55,22 @@
     // Simulate user input of invalid text.
     const invalidSite = 'foobar';
     input.value = invalidSite;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertTrue(input.invalid);
     assertTrue(submit.disabled);
 
     // Entering valid text should clear the error and enable the submit button.
     input.value = 'http://www.example.com';
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
     assertFalse(submit.disabled);
 
     // Wildcard scheme is considered invalid input.
     input.value = '*://www.example.com';
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertTrue(input.invalid);
     assertTrue(submit.disabled);
   });
@@ -79,7 +83,8 @@
     const input = element.shadowRoot!.querySelector('cr-input');
     assertTrue(!!input);
     input.value = newSite;
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     assertFalse(input.invalid);
 
     const whenClosed = eventToPromise('close', element);
diff --git a/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_one_page_test.js b/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_one_page_test.js
index 97e30ade..df401cc8 100644
--- a/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_one_page_test.js
+++ b/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_one_page_test.js
@@ -7,6 +7,7 @@
 import {setContactManagerForTesting} from 'chrome://nearby/shared/nearby_contact_manager.js';
 import {NearbyOnboardingOnePageElement} from 'chrome://nearby/shared/nearby_onboarding_one_page.js';
 import {setNearbyShareSettingsForTesting} from 'chrome://nearby/shared/nearby_share_settings.js';
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
@@ -84,7 +85,8 @@
 
     fakeSettings.setNextDeviceNameResult(
         nearbyShare.mojom.DeviceNameValidationResult.kErrorEmpty);
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     // Allow the validation promise to resolve.
     await waitAfterNextRender(/** @type {!HTMLElement} */ (input));
     assertTrue(input.invalid);
@@ -92,7 +94,8 @@
 
     fakeSettings.setNextDeviceNameResult(
         nearbyShare.mojom.DeviceNameValidationResult.kValid);
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     await waitAfterNextRender(/** @type {!HTMLElement} */ (input));
     assertFalse(input.invalid);
     assertFalse(pageTemplate.actionDisabled);
diff --git a/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_page_test.js b/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_page_test.js
index 1ee153a1..292f33c 100644
--- a/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_page_test.js
+++ b/chrome/test/data/webui/nearby_share/shared/nearby_onboarding_page_test.js
@@ -6,6 +6,7 @@
 
 import {NearbyOnboardingPageElement} from 'chrome://nearby/shared/nearby_onboarding_page.js';
 import {setNearbyShareSettingsForTesting} from 'chrome://nearby/shared/nearby_share_settings.js';
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
@@ -77,7 +78,8 @@
 
     fakeSettings.setNextDeviceNameResult(
         nearbyShare.mojom.DeviceNameValidationResult.kErrorEmpty);
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     // Allow the validation promise to resolve.
     await waitAfterNextRender(/** @type {!HTMLElement} */ (input));
     assertTrue(input.invalid);
@@ -85,7 +87,8 @@
 
     fakeSettings.setNextDeviceNameResult(
         nearbyShare.mojom.DeviceNameValidationResult.kValid);
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     await waitAfterNextRender(/** @type {!HTMLElement} */ (input));
     assertFalse(input.invalid);
     assertFalse(pageTemplate.actionDisabled);
diff --git a/chrome/test/data/webui/settings/appearance_page_test.ts b/chrome/test/data/webui/settings/appearance_page_test.ts
index d7c3bb2..8f3222f 100644
--- a/chrome/test/data/webui/settings/appearance_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_page_test.ts
@@ -331,7 +331,8 @@
 
     homeUrlInput.value = '@@@';
     appearanceBrowserProxy.setValidStartupPageResponse(false);
-    homeUrlInput.$.input.fire('input');
+    homeUrlInput.$.input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
 
     const url = await appearanceBrowserProxy.whenCalled('validateStartupPage');
 
@@ -341,7 +342,8 @@
     assertTrue(homeUrlInput.invalid);
 
     // Should reset to default value on change event.
-    homeUrlInput.$.input.fire('change');
+    homeUrlInput.$.input.dispatchEvent(
+        new CustomEvent('change', {bubbles: true, composed: true}));
     flush();
     assertEquals(homeUrlInput.value, 'test');
   });
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
index 0a1a36d..35782ba 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
@@ -394,7 +394,8 @@
               editDialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = expectedName;
-          nameField.fire('input');
+          nameField.dispatchEvent(
+              new CustomEvent('input', {bubbles: true, composed: true}));
 
           flush();
 
@@ -449,13 +450,15 @@
               editDialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = expectedName;
-          nameField.fire('input');
+          nameField.dispatchEvent(
+              new CustomEvent('input', {bubbles: true, composed: true}));
 
           const addressField =
               editDialog.shadowRoot.querySelector('#printerAddress');
           assertTrue(!!addressField);
           addressField.value = expectedAddress;
-          addressField.fire('input');
+          addressField.dispatchEvent(
+              new CustomEvent('input', {bubbles: true, composed: true}));
 
           assertFalse(
               editDialog.shadowRoot.querySelector('.cancel-button').hidden);
diff --git a/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js
index 9c9ed3b..7768d1f 100644
--- a/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js
@@ -272,7 +272,8 @@
 
     fakeSettings.setNextDeviceNameResult(
         nearbyShare.mojom.DeviceNameValidationResult.kErrorEmpty);
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     // Allow the validation promise to resolve.
     await waitAfterNextRender();
     flush();
@@ -281,7 +282,8 @@
 
     fakeSettings.setNextDeviceNameResult(
         nearbyShare.mojom.DeviceNameValidationResult.kValid);
-    input.fire('input');
+    input.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     await waitAfterNextRender();
     flush();
     assertFalse(input.invalid);
diff --git a/chrome/test/data/webui/settings/cookies_page_test.ts b/chrome/test/data/webui/settings/cookies_page_test.ts
index 926d60d..6de082d 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.ts
+++ b/chrome/test/data/webui/settings/cookies_page_test.ts
@@ -6,7 +6,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {ContentSetting, ContentSettingsTypes,CookiePrimarySetting, SettingsCookiesPageElement, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrLinkRowElement, CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyElementInteractions, Router, routes, SettingsPrefsElement} from 'chrome://settings/settings.js';
+import {CrLinkRowElement, CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyElementInteractions, Router, routes, SettingsPrefsElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
 
@@ -403,3 +403,61 @@
   });
 });
 // </if>
+
+suite('CrSettingsCookiesPageTest_FirstPartySetsUIEnabled', function() {
+  let page: SettingsCookiesPageElement;
+  let settingsPrefs: SettingsPrefsElement;
+
+  suiteSetup(function() {
+    loadTimeData.overrideValues({firstPartySetsUIEnabled: true});
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    document.body.innerHTML = '';
+    page = document.createElement('settings-cookies-page');
+    page.prefs = settingsPrefs.prefs!;
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  test('Disabled Toggle', function() {
+    // Confirm that when the user has not selected the block 3PC setting, the
+    // FPS toggle is disabled.
+    const firstPartySetsToggle =
+        page.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#firstPartySetsToggle')!;
+    page.$.blockThirdParty.click();
+    flush();
+    assertEquals(
+        CookiePrimarySetting.BLOCK_THIRD_PARTY,
+        page.prefs.generated.cookie_primary_setting.value);
+    assertFalse(firstPartySetsToggle.disabled);
+
+    page.$.allowAll.click();
+    flush();
+    assertEquals(
+        CookiePrimarySetting.ALLOW_ALL,
+        page.prefs.generated.cookie_primary_setting.value);
+    assertTrue(firstPartySetsToggle.disabled);
+
+    page.$.blockThirdPartyIncognito.click();
+    flush();
+    assertEquals(
+        CookiePrimarySetting.BLOCK_THIRD_PARTY_INCOGNITO,
+        page.prefs.generated.cookie_primary_setting.value);
+    assertTrue(firstPartySetsToggle.disabled);
+
+    page.$.blockAll.click();
+    flush();
+    assertEquals(
+        CookiePrimarySetting.BLOCK_ALL,
+        page.prefs.generated.cookie_primary_setting.value);
+    assertTrue(firstPartySetsToggle.disabled);
+  });
+});
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 0655b57..3a97d35 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -637,6 +637,10 @@
   runMochaSuite('CrSettingsCookiesPageTest');
 });
 
+TEST_F('CrSettingsCookiesPageTest', 'FirstPartySetsUIEnabled', function() {
+  runMochaSuite('CrSettingsCookiesPageTest_FirstPartySetsUIEnabled');
+});
+
 // Flaky on MacOS bots and times out on Linux Dbg: https://crbug.com/1240747
 GEN('#if (BUILDFLAG(IS_MAC)) || (BUILDFLAG(IS_LINUX) && !defined(NDEBUG))');
 GEN('#define MAYBE_ConsolidatedControlsEnabled DISABLED_ConsolidatedControlsEnabled');
diff --git a/chrome/test/data/webui/settings/payments_section_interactive_test.ts b/chrome/test/data/webui/settings/payments_section_interactive_test.ts
index c911c9e..9fcbf6a6 100644
--- a/chrome/test/data/webui/settings/payments_section_interactive_test.ts
+++ b/chrome/test/data/webui/settings/payments_section_interactive_test.ts
@@ -18,7 +18,8 @@
  */
 function typeInNickname(nicknameInput: CrInputElement, nickname: string) {
   nicknameInput.value = nickname;
-  nicknameInput.fire('input');
+  nicknameInput.dispatchEvent(
+      new CustomEvent('input', {bubbles: true, composed: true}));
 }
 
 suite('PaymentsSectionCreditCardEditDialogTest', function() {
diff --git a/chrome/test/data/webui/settings/people_page_manage_profile_test.ts b/chrome/test/data/webui/settings/people_page_manage_profile_test.ts
index 51fd7c8..be1ba97 100644
--- a/chrome/test/data/webui/settings/people_page_manage_profile_test.ts
+++ b/chrome/test/data/webui/settings/people_page_manage_profile_test.ts
@@ -149,7 +149,8 @@
     assertEquals('Initial Fake Name', nameField.value);
 
     nameField.value = 'New Name';
-    nameField.fire('change');
+    nameField.dispatchEvent(
+        new CustomEvent('change', {bubbles: true, composed: true}));
 
     const args = await browserProxy.whenCalled('setProfileName');
     assertEquals('New Name', args[0]);
diff --git a/chrome/test/data/webui/settings/site_list_tests.ts b/chrome/test/data/webui/settings/site_list_tests.ts
index 4cf200c..56649bf 100644
--- a/chrome/test/data/webui/settings/site_list_tests.ts
+++ b/chrome/test/data/webui/settings/site_list_tests.ts
@@ -1032,7 +1032,8 @@
 
     // Simulate user input of whitespace only text.
     input!.value = '  ';
-    input!.fire('input');
+    input!.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
     flush();
     assertTrue(actionButton.disabled);
     assertTrue(input!.invalid);
@@ -1041,7 +1042,8 @@
     browserProxy.setIsPatternValidForType(false);
     const expectedPattern = '*';
     input!.value = expectedPattern;
-    input!.fire('input');
+    input!.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
 
     return browserProxy.whenCalled('isPatternValidForType').then(function([
       pattern,
@@ -1133,7 +1135,8 @@
     browserProxy.setIsPatternValidForType(false);
     const expectedPattern = 'foobarbaz';
     input!.value = expectedPattern;
-    input!.fire('input');
+    input!.dispatchEvent(
+        new CustomEvent('input', {bubbles: true, composed: true}));
 
     return browserProxy.whenCalled('isPatternValidForType').then(function([
       pattern,
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index e989bb67..36694feb 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -84,25 +84,6 @@
   sources = common_resources
 }
 
-android_library("cast_audio_manager_java") {
-  java_src_dir = "//chromecast/browser/android/apk/src"
-
-  alternative_android_sdk_dep =
-      "//third_party/android_sdk:public_framework_system_java"
-
-  sources = [
-    "$java_src_dir/org/chromium/chromecast/shell/CastAudioFocusRequest.java",
-    "$java_src_dir/org/chromium/chromecast/shell/CastAudioManager.java",
-  ]
-  deps = [
-    "//base:base_java",
-    "//base:jni_java",
-    "//build/android:build_java",
-    "//chromecast/base:base_java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-  ]
-}
-
 android_library("cast_command_line_helper_java") {
   java_src_dir = "//chromecast/browser/android/apk/src"
   sources = [
@@ -142,7 +123,6 @@
 }
 
 common_android_library_deps = [
-  ":cast_audio_manager_java",
   ":cast_command_line_helper_java",
   ":cast_intents_java",
   ":reactive_android_java",
@@ -244,8 +224,6 @@
 robolectric_binary("cast_shell_junit_tests") {
   sources = [
     "junit/src/org/chromium/chromecast/shell/AsyncTaskRunnerTest.java",
-    "junit/src/org/chromium/chromecast/shell/CastAudioFocusRequestTest.java",
-    "junit/src/org/chromium/chromecast/shell/CastAudioManagerTest.java",
     "junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java",
     "junit/src/org/chromium/chromecast/shell/CastWebContentsComponentTest.java",
     "junit/src/org/chromium/chromecast/shell/CastWebContentsIntentUtilsTest.java",
@@ -260,7 +238,6 @@
   srcjar_deps = [ ":cast_shell_build_config_gen" ]
 
   deps = [
-    ":cast_audio_manager_java",
     ":cast_intents_java",
     ":cast_shell_java",
     ":reactive_android_java",
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioFocusRequest.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioFocusRequest.java
deleted file mode 100644
index b624bbd5..0000000
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioFocusRequest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chromecast.shell;
-
-import android.media.AudioAttributes;
-import android.media.AudioFocusRequest;
-import android.media.AudioManager;
-import android.os.Build;
-
-import androidx.annotation.NonNull;
-
-import org.chromium.base.Log;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Wrapper for Cast code to pass parameter to AudioFocus methods.
- * This maintains backwards compatibility with old APIs - requestAudioFocus() and
- * abandonAudioFocus()
- */
-public class CastAudioFocusRequest {
-    private static final String TAG = "CastAudioFocus";
-    private AudioFocusRequest mAudioFocusRequest;
-    private AudioAttributes mAudioAttributes;
-    private int mFocusGain;
-    private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener;
-
-    CastAudioFocusRequest(AudioFocusRequest audioFocusRequest) {
-        mAudioFocusRequest = audioFocusRequest;
-    }
-
-    CastAudioFocusRequest(AudioAttributes audioAttributes, int focusGain,
-            AudioManager.OnAudioFocusChangeListener l) {
-        mAudioAttributes = audioAttributes;
-        mFocusGain = focusGain;
-        mAudioFocusChangeListener = l;
-    }
-
-    AudioFocusRequest getAudioFocusRequest() {
-        return mAudioFocusRequest;
-    }
-
-    private int getStreamType() {
-        if (mAudioAttributes != null) {
-            switch (mAudioAttributes.getContentType()) {
-                case AudioAttributes.CONTENT_TYPE_MOVIE:
-                case AudioAttributes.CONTENT_TYPE_MUSIC:
-                    return AudioManager.STREAM_MUSIC;
-                case AudioAttributes.CONTENT_TYPE_SONIFICATION:
-                    return AudioManager.STREAM_ALARM;
-                case AudioAttributes.CONTENT_TYPE_SPEECH:
-                    return AudioManager.STREAM_VOICE_CALL;
-                case AudioAttributes.CONTENT_TYPE_UNKNOWN:
-                default:
-                    return AudioManager.STREAM_SYSTEM;
-            }
-        }
-        return 0;
-    }
-
-    void setAudioFocusChangeListener(AudioManager.OnAudioFocusChangeListener l) {
-        mAudioFocusChangeListener = l;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && mAudioFocusRequest != null) {
-            mAudioFocusRequest = new AudioFocusRequest.Builder(mAudioFocusRequest)
-                                         .setOnAudioFocusChangeListener(mAudioFocusChangeListener)
-                                         .build();
-        }
-    }
-
-    int request(AudioManager audioManager) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            return audioManager.requestAudioFocus(mAudioFocusRequest);
-        } else {
-            return audioManager.requestAudioFocus(
-                    mAudioFocusChangeListener, getStreamType(), mFocusGain);
-        }
-    }
-
-    int abandon(AudioManager audioManager) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            return audioManager.abandonAudioFocusRequest(mAudioFocusRequest);
-        } else {
-            return audioManager.abandonAudioFocus(mAudioFocusChangeListener);
-        }
-    }
-
-    /**
-     * Backwards compatible builder method to create CastAudioFocusRequest object.
-     */
-    public static class Builder {
-        private AudioAttributes mAudioAttributes;
-        private int mFocusGain;
-        private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener;
-        private Set<Integer> mValidFocusGainValues;
-
-        public Builder() {
-            mAudioAttributes = null;
-            mFocusGain = 0;
-            mAudioFocusChangeListener = null;
-            mValidFocusGainValues = new HashSet<Integer>();
-            mValidFocusGainValues.add(AudioManager.AUDIOFOCUS_GAIN);
-            mValidFocusGainValues.add(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-            mValidFocusGainValues.add(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
-            mValidFocusGainValues.add(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE);
-        }
-
-        public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes audioAttributes) {
-            mAudioAttributes = audioAttributes;
-            return this;
-        }
-
-        public @NonNull Builder setFocusGain(int focusGain) {
-            if (mValidFocusGainValues.contains(focusGain)) {
-                mFocusGain = focusGain;
-            } else {
-                Log.e(TAG, "Invalid focus gain value " + focusGain);
-                mFocusGain = AudioManager.AUDIOFOCUS_GAIN;
-            }
-            return this;
-        }
-
-        public @NonNull Builder setAudioFocusChangeListener(
-                AudioManager.OnAudioFocusChangeListener l) {
-            mAudioFocusChangeListener = l;
-            return this;
-        }
-
-        public CastAudioFocusRequest build() {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                AudioFocusRequest.Builder builder = new AudioFocusRequest.Builder(mFocusGain);
-                if (mAudioAttributes != null) {
-                    builder = builder.setAudioAttributes(mAudioAttributes);
-                }
-                if (mAudioFocusChangeListener != null) {
-                    builder = builder.setOnAudioFocusChangeListener(mAudioFocusChangeListener);
-                }
-                AudioFocusRequest audioFocusRequest = builder.build();
-                return new CastAudioFocusRequest(audioFocusRequest);
-            } else {
-                return new CastAudioFocusRequest(
-                        mAudioAttributes, mFocusGain, mAudioFocusChangeListener);
-            }
-        }
-    }
-}
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastAudioFocusRequestTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastAudioFocusRequestTest.java
deleted file mode 100644
index 33e5eff..0000000
--- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastAudioFocusRequestTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chromecast.shell;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Build;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.annotation.Config;
-
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests for CastAudioFocusRequest.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public final class CastAudioFocusRequestTest {
-    private @Mock AudioManager mAudioManager;
-
-    @Before
-    public void setUp() {
-        mAudioManager = mock(AudioManager.class);
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testOldAudioFocusRequest() {
-        AudioAttributes audioAttributes =
-                new AudioAttributes.Builder()
-                        .setUsage(AudioAttributes.USAGE_MEDIA)
-                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-                        .build();
-
-        CastAudioFocusRequest castAudioFocusRequest =
-                new CastAudioFocusRequest.Builder()
-                        .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
-                        .setAudioFocusChangeListener(null)
-                        .setAudioAttributes(audioAttributes)
-                        .build();
-        castAudioFocusRequest.request(mAudioManager);
-        verify(mAudioManager)
-                .requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testOldAbandonAudioFocus() {
-        AudioAttributes audioAttributes =
-                new AudioAttributes.Builder()
-                        .setUsage(AudioAttributes.USAGE_MEDIA)
-                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-                        .build();
-
-        CastAudioFocusRequest castAudioFocusRequest =
-                new CastAudioFocusRequest.Builder()
-                        .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
-                        .setAudioFocusChangeListener(null)
-                        .setAudioAttributes(audioAttributes)
-                        .build();
-        castAudioFocusRequest.abandon(mAudioManager);
-        verify(mAudioManager).abandonAudioFocus(null);
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.O)
-    public void testNewAudioFocusRequest() {
-        AudioAttributes audioAttributes =
-                new AudioAttributes.Builder()
-                        .setUsage(AudioAttributes.USAGE_MEDIA)
-                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-                        .build();
-
-        CastAudioFocusRequest castAudioFocusRequest =
-                new CastAudioFocusRequest.Builder()
-                        .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
-                        .setAudioFocusChangeListener(null)
-                        .setAudioAttributes(audioAttributes)
-                        .build();
-        castAudioFocusRequest.request(mAudioManager);
-        verify(mAudioManager).requestAudioFocus(castAudioFocusRequest.getAudioFocusRequest());
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.O)
-    public void testNewAbandonAudioFocus() {
-        AudioAttributes audioAttributes =
-                new AudioAttributes.Builder()
-                        .setUsage(AudioAttributes.USAGE_MEDIA)
-                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-                        .build();
-
-        CastAudioFocusRequest castAudioFocusRequest =
-                new CastAudioFocusRequest.Builder()
-                        .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
-                        .setAudioFocusChangeListener(null)
-                        .setAudioAttributes(audioAttributes)
-                        .build();
-        castAudioFocusRequest.abandon(mAudioManager);
-        verify(mAudioManager)
-                .abandonAudioFocusRequest(castAudioFocusRequest.getAudioFocusRequest());
-    }
-}
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastAudioManagerTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastAudioManagerTest.java
deleted file mode 100644
index 4497e52..0000000
--- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastAudioManagerTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chromecast.shell;
-
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Build;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowAudioManager;
-
-import org.chromium.chromecast.base.Controller;
-import org.chromium.chromecast.base.Observable;
-import org.chromium.chromecast.base.ReactiveRecorder;
-import org.chromium.testing.local.LocalRobolectricTestRunner;
-
-/**
- * Tests for CastAudioManager.
- */
-@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class CastAudioManagerTest {
-    // An example request that can be provided to requestAudioFocus().
-    private static CastAudioFocusRequest buildFocusRequest() {
-        return new CastAudioFocusRequest.Builder()
-                .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
-                .setAudioAttributes(new AudioAttributes.Builder()
-                                            .setUsage(AudioAttributes.USAGE_MEDIA)
-                                            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-                                            .build())
-                .build();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testAudioFocusScopeDeactivatesWhenRequestGranted() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        requestAudioFocusState.set(buildFocusRequest());
-        lostAudioFocusRecorder.verify().closed(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testAudioFocusLostWhenFocusRequestStateIsReset() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        requestAudioFocusState.set(buildFocusRequest());
-        shadowAudioManager.getLastAudioFocusRequest().listener.onAudioFocusChange(
-                AudioManager.AUDIOFOCUS_GAIN);
-        lostAudioFocusRecorder.verify().closed(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        requestAudioFocusState.reset();
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NORMAL).end();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testAudioFocusScopeActivatedWhenAudioFocusIsLostButRequestStillActive() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        requestAudioFocusState.set(buildFocusRequest());
-        AudioManager.OnAudioFocusChangeListener listener =
-                shadowAudioManager.getLastAudioFocusRequest().listener;
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN);
-        lostAudioFocusRecorder.verify().closed(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_LOSS);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NORMAL).end();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testAudioFocusScopeWhenAudioFocusIsLostAndRegained() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        requestAudioFocusState.set(buildFocusRequest());
-        AudioManager.OnAudioFocusChangeListener listener =
-                shadowAudioManager.getLastAudioFocusRequest().listener;
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN);
-        lostAudioFocusRecorder.verify().closed(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_LOSS);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NORMAL).end();
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN);
-        lostAudioFocusRecorder.verify().closed(CastAudioManager.AudioFocusLoss.NORMAL).end();
-    }
-
-    @Test
-    public void testAudioFocusNotGainedIfRequestNotActivated() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.NOT_REQUESTED).end();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testNoAudioFocusLossIfRequestGrantedImmediately() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        requestAudioFocusState.set(buildFocusRequest());
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        lostAudioFocusRecorder.verify().end();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testTransientAudioFocusLoss() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        requestAudioFocusState.set(buildFocusRequest());
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        AudioManager.OnAudioFocusChangeListener listener =
-                shadowAudioManager.getLastAudioFocusRequest().listener;
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
-        lostAudioFocusRecorder.verify().opened(CastAudioManager.AudioFocusLoss.TRANSIENT).end();
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN);
-        lostAudioFocusRecorder.verify().closed(CastAudioManager.AudioFocusLoss.TRANSIENT).end();
-    }
-
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testTransientCanDuckAudioFocusLoss() {
-        CastAudioManager audioManager =
-                CastAudioManager.getAudioManager(RuntimeEnvironment.application);
-        ShadowAudioManager shadowAudioManager = Shadows.shadowOf(audioManager.getInternal());
-        Controller<CastAudioFocusRequest> requestAudioFocusState = new Controller<>();
-        requestAudioFocusState.set(buildFocusRequest());
-        Observable<CastAudioManager.AudioFocusLoss> lostAudioFocusState =
-                audioManager.requestAudioFocusWhen(requestAudioFocusState);
-        ReactiveRecorder lostAudioFocusRecorder = ReactiveRecorder.record(lostAudioFocusState);
-        AudioManager.OnAudioFocusChangeListener listener =
-                shadowAudioManager.getLastAudioFocusRequest().listener;
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK);
-        lostAudioFocusRecorder.verify()
-                .opened(CastAudioManager.AudioFocusLoss.TRANSIENT_CAN_DUCK)
-                .end();
-        listener.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN);
-        lostAudioFocusRecorder.verify()
-                .closed(CastAudioManager.AudioFocusLoss.TRANSIENT_CAN_DUCK)
-                .end();
-    }
-}
diff --git a/chromeos/ash/components/dbus/cros_disks/cros_disks_client.cc b/chromeos/ash/components/dbus/cros_disks/cros_disks_client.cc
index 4b6259ec..ab66802 100644
--- a/chromeos/ash/components/dbus/cros_disks/cros_disks_client.cc
+++ b/chromeos/ash/components/dbus/cros_disks/cros_disks_client.cc
@@ -695,6 +695,71 @@
   return out << std::underlying_type_t<MountError>(error);
 }
 
+std::ostream& operator<<(std::ostream& out, const RenameError error) {
+  switch (error) {
+#define PRINT_ERROR(s) \
+  case RenameError::s: \
+    return out << #s;
+    PRINT_ERROR(kNone)
+    PRINT_ERROR(kUnknown)
+    PRINT_ERROR(kInternal)
+    PRINT_ERROR(kInvalidDevicePath)
+    PRINT_ERROR(kDeviceBeingRenamed)
+    PRINT_ERROR(kUnsupportedFilesystem)
+    PRINT_ERROR(kRenameProgramNotFound)
+    PRINT_ERROR(kRenameProgramFailed)
+    PRINT_ERROR(kDeviceNotAllowed)
+    PRINT_ERROR(kLongName)
+    PRINT_ERROR(kInvalidCharacter)
+#undef PRINT_ERROR
+  }
+
+  return out << std::underlying_type_t<RenameError>(error);
+}
+
+std::ostream& operator<<(std::ostream& out, const FormatError error) {
+  switch (error) {
+#define PRINT_ERROR(s) \
+  case FormatError::s: \
+    return out << #s;
+    PRINT_ERROR(kNone)
+    PRINT_ERROR(kUnknown)
+    PRINT_ERROR(kInternal)
+    PRINT_ERROR(kInvalidDevicePath)
+    PRINT_ERROR(kDeviceBeingFormatted)
+    PRINT_ERROR(kUnsupportedFilesystem)
+    PRINT_ERROR(kFormatProgramNotFound)
+    PRINT_ERROR(kFormatProgramFailed)
+    PRINT_ERROR(kDeviceNotAllowed)
+    PRINT_ERROR(kInvalidOptions)
+    PRINT_ERROR(kLongName)
+    PRINT_ERROR(kInvalidCharacter)
+    PRINT_ERROR(kCount)
+#undef PRINT_ERROR
+  }
+
+  return out << std::underlying_type_t<FormatError>(error);
+}
+
+std::ostream& operator<<(std::ostream& out, const PartitionError error) {
+  switch (error) {
+#define PRINT_ERROR(s)    \
+  case PartitionError::s: \
+    return out << #s;
+    PRINT_ERROR(kNone)
+    PRINT_ERROR(kUnknown)
+    PRINT_ERROR(kInternal)
+    PRINT_ERROR(kInvalidDevicePath)
+    PRINT_ERROR(kDeviceBeingPartitioned)
+    PRINT_ERROR(kProgramNotFound)
+    PRINT_ERROR(kProgramFailed)
+    PRINT_ERROR(kDeviceNotAllowed)
+#undef PRINT_ERROR
+  }
+
+  return out << std::underlying_type_t<PartitionError>(error);
+}
+
 std::ostream& operator<<(std::ostream& out, const MountEntry& entry) {
   return out << "error_code = " << entry.error_code << ", source_path = '"
              << entry.source_path << "', mount_type = " << entry.mount_type
diff --git a/chromeos/ash/components/dbus/cros_disks/cros_disks_client.h b/chromeos/ash/components/dbus/cros_disks/cros_disks_client.h
index 62b791c..9c0aa636 100644
--- a/chromeos/ash/components/dbus/cros_disks/cros_disks_client.h
+++ b/chromeos/ash/components/dbus/cros_disks/cros_disks_client.h
@@ -105,25 +105,34 @@
   kInvalidCharacter,
 };
 
+// Output operator for logging.
+COMPONENT_EXPORT(ASH_DBUS_CROS_DISKS)
+std::ostream& operator<<(std::ostream& out, RenameError error);
+
 // Format error reported by cros-disks.
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
+// See enum CrosDisksClientFormatError in tools/metrics/histograms/enums.xml.
 enum class FormatError {
-  kNone,
-  kUnknown,
-  kInternal,
-  kInvalidDevicePath,
-  kDeviceBeingFormatted,
-  kUnsupportedFilesystem,
-  kFormatProgramNotFound,
-  kFormatProgramFailed,
-  kDeviceNotAllowed,
-  kInvalidOptions,
-  kLongName,
-  kInvalidCharacter,
-  kCount,
+  kNone = 0,
+  kUnknown = 1,
+  kInternal = 2,
+  kInvalidDevicePath = 3,
+  kDeviceBeingFormatted = 4,
+  kUnsupportedFilesystem = 5,
+  kFormatProgramNotFound = 6,
+  kFormatProgramFailed = 7,
+  kDeviceNotAllowed = 8,
+  kInvalidOptions = 9,
+  kLongName = 10,
+  kInvalidCharacter = 11,
+  kCount = 12,
 };
 
+// Output operator for logging.
+COMPONENT_EXPORT(ASH_DBUS_CROS_DISKS)
+std::ostream& operator<<(std::ostream& out, FormatError error);
+
 // Partition error reported by cros-disks.
 enum class PartitionError {
   kNone = 0,
@@ -136,6 +145,10 @@
   kDeviceNotAllowed = 7,
 };
 
+// Output operator for logging.
+COMPONENT_EXPORT(ASH_DBUS_CROS_DISKS)
+std::ostream& operator<<(std::ostream& out, PartitionError error);
+
 // Event type each corresponding to a signal sent from cros-disks.
 enum class MountEventType {
   kDiskAdded,
diff --git a/chromeos/ash/components/dbus/rmad/rmad_client.cc b/chromeos/ash/components/dbus/rmad/rmad_client.cc
index d91725b..b720dd2 100644
--- a/chromeos/ash/components/dbus/rmad/rmad_client.cc
+++ b/chromeos/ash/components/dbus/rmad/rmad_client.cc
@@ -313,13 +313,16 @@
   DCHECK(!reader.HasMoreData());
   int32_t status;
   double progress;
-  if (!sub_reader.PopInt32(&status) || !sub_reader.PopDouble(&progress)) {
+  int32_t error;
+  if (!sub_reader.PopInt32(&status) || !sub_reader.PopDouble(&progress) ||
+      !sub_reader.PopInt32(&error)) {
     LOG(ERROR) << "Unable to decode signal for " << signal->GetMember();
     return;
   }
   rmad::ProvisionStatus signal_proto;
   signal_proto.set_status(static_cast<rmad::ProvisionStatus::Status>(status));
   signal_proto.set_progress(progress);
+  signal_proto.set_error(static_cast<rmad::ProvisionStatus::Error>(error));
   for (auto& observer : observers_) {
     observer.ProvisioningProgress(signal_proto);
   }
@@ -362,13 +365,16 @@
   DCHECK(!reader.HasMoreData());
   int32_t status;
   double progress;
-  if (!sub_reader.PopInt32(&status) || !sub_reader.PopDouble(&progress)) {
+  int32_t error;
+  if (!sub_reader.PopInt32(&status) || !sub_reader.PopDouble(&progress) ||
+      !sub_reader.PopInt32(&error)) {
     LOG(ERROR) << "Unable to decode signal for " << signal->GetMember();
     return;
   }
   rmad::FinalizeStatus signal_proto;
   signal_proto.set_status(static_cast<rmad::FinalizeStatus::Status>(status));
   signal_proto.set_progress(progress);
+  signal_proto.set_error(static_cast<rmad::FinalizeStatus::Error>(error));
   for (auto& observer : observers_) {
     observer.FinalizationProgress(signal_proto);
   }
diff --git a/chromeos/ash/components/dbus/rmad/rmad_client_unittest.cc b/chromeos/ash/components/dbus/rmad/rmad_client_unittest.cc
index ca8b805b..4b92c69 100644
--- a/chromeos/ash/components/dbus/rmad/rmad_client_unittest.cc
+++ b/chromeos/ash/components/dbus/rmad/rmad_client_unittest.cc
@@ -153,7 +153,8 @@
 
   // Passes a provisioning progress signal to |client_|.
   void EmitProvisioningProgressSignal(rmad::ProvisionStatus::Status status,
-                                      double progress) {
+                                      double progress,
+                                      rmad::ProvisionStatus::Error error) {
     dbus::Signal signal(rmad::kRmadInterfaceName,
                         rmad::kProvisioningProgressSignal);
     dbus::MessageWriter writer(&signal);
@@ -161,6 +162,7 @@
     writer.OpenStruct(&struct_writer);
     struct_writer.AppendInt32(static_cast<int32_t>(status));
     struct_writer.AppendDouble(progress);
+    struct_writer.AppendInt32(static_cast<int32_t>(error));
     writer.CloseContainer(&struct_writer);
     EmitSignal(&signal);
   }
@@ -181,7 +183,8 @@
 
   // Passes a finalization status signal to |client_|.
   void EmitFinalizationProgressSignal(rmad::FinalizeStatus::Status status,
-                                      double progress) {
+                                      double progress,
+                                      rmad::FinalizeStatus::Error error) {
     dbus::Signal signal(rmad::kRmadInterfaceName,
                         rmad::kFinalizeProgressSignal);
     dbus::MessageWriter writer(&signal);
@@ -189,6 +192,7 @@
     writer.OpenStruct(&struct_writer);
     struct_writer.AppendInt32(static_cast<int32_t>(status));
     struct_writer.AppendDouble(progress);
+    struct_writer.AppendInt32(static_cast<int32_t>(error));
     writer.CloseContainer(&struct_writer);
     EmitSignal(&signal);
   }
@@ -867,11 +871,29 @@
   TestObserver observer_1(client_);
 
   EmitProvisioningProgressSignal(
-      rmad::ProvisionStatus::RMAD_PROVISION_STATUS_IN_PROGRESS, 0.25);
+      rmad::ProvisionStatus::RMAD_PROVISION_STATUS_IN_PROGRESS, 0.25,
+      rmad::ProvisionStatus::RMAD_PROVISION_ERROR_UNKNOWN);
   EXPECT_EQ(observer_1.num_provisioning_progress(), 1);
   EXPECT_EQ(observer_1.last_provisioning_status().status(),
             rmad::ProvisionStatus::RMAD_PROVISION_STATUS_IN_PROGRESS);
   EXPECT_EQ(observer_1.last_provisioning_status().progress(), 0.25);
+  EXPECT_EQ(observer_1.last_provisioning_status().error(),
+            rmad::ProvisionStatus::RMAD_PROVISION_ERROR_UNKNOWN);
+}
+
+// Tests that synchronous observers are notified about provisioning errors.
+TEST_F(RmadClientTest, ProvisioningErrors) {
+  TestObserver observer_1(client_);
+
+  EmitProvisioningProgressSignal(
+      rmad::ProvisionStatus::RMAD_PROVISION_STATUS_FAILED_BLOCKING, 0.25,
+      rmad::ProvisionStatus::RMAD_PROVISION_ERROR_CR50);
+  EXPECT_EQ(observer_1.num_provisioning_progress(), 1);
+  EXPECT_EQ(observer_1.last_provisioning_status().status(),
+            rmad::ProvisionStatus::RMAD_PROVISION_STATUS_FAILED_BLOCKING);
+  EXPECT_EQ(observer_1.last_provisioning_status().progress(), 0.25);
+  EXPECT_EQ(observer_1.last_provisioning_status().error(),
+            rmad::ProvisionStatus::RMAD_PROVISION_ERROR_CR50);
 }
 
 // Tests that synchronous observers are notified about provisioning progress.
@@ -917,26 +939,42 @@
   EXPECT_EQ(observer_1.last_hardware_verification_result().error_str(), "ok");
 }
 
-// Tests that synchronous observers are notified about hardware verification
-// status.
+// Tests that synchronous observers are notified about finalization status.
 TEST_F(RmadClientTest, FinalizationProgress) {
   TestObserver observer_1(client_);
 
   EmitFinalizationProgressSignal(
-      rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_IN_PROGRESS, 0.5);
+      rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_IN_PROGRESS, 0.5,
+      rmad::FinalizeStatus::RMAD_FINALIZE_ERROR_UNKNOWN);
   EXPECT_EQ(observer_1.num_finalization_progress(), 1);
   EXPECT_EQ(observer_1.last_finalization_progress().status(),
             rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_IN_PROGRESS);
   EXPECT_EQ(observer_1.last_finalization_progress().progress(), 0.5);
 
   EmitFinalizationProgressSignal(
-      rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_COMPLETE, 1.0);
+      rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_COMPLETE, 1.0,
+      rmad::FinalizeStatus::RMAD_FINALIZE_ERROR_UNKNOWN);
   EXPECT_EQ(observer_1.num_finalization_progress(), 2);
   EXPECT_EQ(observer_1.last_finalization_progress().status(),
             rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_COMPLETE);
   EXPECT_EQ(observer_1.last_finalization_progress().progress(), 1.0);
 }
 
+// Tests that synchronous observers are notified about finalization errors.
+TEST_F(RmadClientTest, FinalizationErrors) {
+  TestObserver observer_1(client_);
+
+  EmitFinalizationProgressSignal(
+      rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_FAILED_BLOCKING, 0.5,
+      rmad::FinalizeStatus::RMAD_FINALIZE_ERROR_CR50);
+  EXPECT_EQ(observer_1.num_finalization_progress(), 1);
+  EXPECT_EQ(observer_1.last_finalization_progress().status(),
+            rmad::FinalizeStatus::RMAD_FINALIZE_STATUS_FAILED_BLOCKING);
+  EXPECT_EQ(observer_1.last_finalization_progress().progress(), 0.5);
+  EXPECT_EQ(observer_1.last_finalization_progress().error(),
+            rmad::FinalizeStatus::RMAD_FINALIZE_ERROR_CR50);
+}
+
 TEST_F(RmadClientTest, RoFirmwareUpdateProgress) {
   TestObserver observer_1(client_);
 
diff --git a/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.cc b/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.cc
index 20d6054..edba5fc 100644
--- a/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.cc
+++ b/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.cc
@@ -122,6 +122,7 @@
 }
 
 void FakeUpdateEngineClient::ApplyDeferredUpdate(
+    bool shutdown_after_update,
     base::OnceClosure failure_callback) {
   apply_deferred_update_count_++;
 }
diff --git a/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h b/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h
index 2c19558f..a665e5e 100644
--- a/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h
+++ b/chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h
@@ -57,7 +57,8 @@
   void ToggleFeature(const std::string& feature, bool enable) override;
   void IsFeatureEnabled(const std::string& feature,
                         IsFeatureEnabledCallback callback) override;
-  void ApplyDeferredUpdate(base::OnceClosure failure_callback) override;
+  void ApplyDeferredUpdate(bool shutdown_after_update,
+                           base::OnceClosure failure_callback) override;
   // Pushes update_engine::StatusResult in the queue to test changing status.
   // GetLastStatus() returns the status set by this method in FIFO order.
   // See set_default_status().
diff --git a/chromeos/ash/components/dbus/update_engine/update_engine_client.cc b/chromeos/ash/components/dbus/update_engine/update_engine_client.cc
index 1fdde1e..97946168 100644
--- a/chromeos/ash/components/dbus/update_engine/update_engine_client.cc
+++ b/chromeos/ash/components/dbus/update_engine/update_engine_client.cc
@@ -265,15 +265,24 @@
                        std::move(callback)));
   }
 
-  void ApplyDeferredUpdate(base::OnceClosure failure_callback) override {
+  void ApplyDeferredUpdate(bool shutdown_after_update,
+                           base::OnceClosure failure_callback) override {
+    update_engine::ApplyUpdateConfig config;
+    config.set_done_action(shutdown_after_update
+                               ? update_engine::UpdateDoneAction::SHUTDOWN
+                               : update_engine::UpdateDoneAction::REBOOT);
     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
-                                 update_engine::kApplyDeferredUpdate);
+                                 update_engine::kApplyDeferredUpdateAdvanced);
     dbus::MessageWriter writer(&method_call);
+    if (!writer.AppendProtoAsArrayOfBytes(config)) {
+      LOG(ERROR) << "Failed to encode ApplyUpdateConfig protobuf.";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, std::move(failure_callback));
+      return;
+    }
 
     VLOG(1) << "Requesting UpdateEngine to apply deferred update.";
 
-    // TODO(yuanpengni): Add an option to shutdown after applied deferred
-    // update.
     update_engine_proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&UpdateEngineClientImpl::OnApplyDeferredUpdate,
@@ -708,8 +717,10 @@
     std::move(callback).Run(absl::nullopt);
   }
 
-  void ApplyDeferredUpdate(base::OnceClosure failure_callback) override {
-    VLOG(1) << "Applying deferred update.";
+  void ApplyDeferredUpdate(bool shutdown_after_update,
+                           base::OnceClosure failure_callback) override {
+    VLOG(1) << "Applying deferred update and "
+            << (shutdown_after_update ? "shutdown." : "reboot.");
   }
 
  private:
diff --git a/chromeos/ash/components/dbus/update_engine/update_engine_client.h b/chromeos/ash/components/dbus/update_engine/update_engine_client.h
index d7376059..89c7fa8 100644
--- a/chromeos/ash/components/dbus/update_engine/update_engine_client.h
+++ b/chromeos/ash/components/dbus/update_engine/update_engine_client.h
@@ -189,8 +189,11 @@
   virtual void IsFeatureEnabled(const std::string& feature,
                                 IsFeatureEnabledCallback callback) = 0;
 
-  // Apply a downloaded but deferred update. Runs callback on failure.
-  virtual void ApplyDeferredUpdate(base::OnceClosure failure_callback) = 0;
+  // Apply a downloaded but deferred update. When `shutdown_after_update` is set
+  // to true, shutdown after applying the update, otherwise reboot. The callback
+  // will run on dbus call failure.
+  virtual void ApplyDeferredUpdate(bool shutdown_after_update,
+                                   base::OnceClosure failure_callback) = 0;
 
  protected:
   // Initialize() should be used instead.
diff --git a/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match.h b/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match.h
index b0861f8..0a2471e4 100644
--- a/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match.h
+++ b/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match.h
@@ -20,9 +20,6 @@
 // other. The higher the relevance score, the better the two strings are
 // matched. Matched portions of text are stored as index ranges.
 //
-// TODO(crbug.com/1018613): each of these functions have too many input params,
-// we should revise the structure and remove unnecessary ones.
-//
 // TODO(crbug.com/1336160): Terminology (for example: relevance vs. ratio) is
 // confusing and could be clarified.
 class FuzzyTokenizedStringMatch {
diff --git a/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match_unittest.cc b/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match_unittest.cc
index 5aca6d6c..4707f695 100644
--- a/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match_unittest.cc
+++ b/chromeos/ash/components/string_matching/fuzzy_tokenized_string_match_unittest.cc
@@ -146,7 +146,6 @@
 /**********************************************************************
  * Benchmarking section 1 - Abstract test cases                       *
  **********************************************************************/
-// TODO(crbug.com/1336160): Expand abstract benchmarking tests.
 
 TEST_F(FuzzyTokenizedStringMatchTest, BenchmarkCaseInsensitivity) {
   std::u16string text = u"abcde";
diff --git a/chromeos/ash/components/string_matching/tokenized_string_match.cc b/chromeos/ash/components/string_matching/tokenized_string_match.cc
index 8b00d19..6cba3577 100644
--- a/chromeos/ash/components/string_matching/tokenized_string_match.cc
+++ b/chromeos/ash/components/string_matching/tokenized_string_match.cc
@@ -69,9 +69,6 @@
   // relevance (roughly, each keystroke) is worth less than the last. This means
   // that typing a few characters of a word is enough to promote matches very
   // high, with any subsequent characters being worth comparatively less.
-  // TODO(mgiuca): This doesn't really play well with Omnibox results, since as
-  // you type more characters, the app/omnibox results tend to jump over each
-  // other.
   relevance_ = 1.0 - std::pow(0.5, relevance_);
 
   return relevance_;
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn b/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn
index edce0db..a72f7d38 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn
+++ b/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn
@@ -14,11 +14,13 @@
   ]
   deps = [
     "//base",
+    "//chromeos/components/mojo_service_manager",
     "//content/public/browser:browser",
     "//ui/events/devices",
     "//ui/events/ozone/evdev:event_device_info",
   ]
   public_deps = [ "//chromeos/ash/services/cros_healthd/private/mojom" ]
+  data_deps = [ ":mojo_service_manager_policy" ]
   defines = []
   if (is_chromeos_device) {
     defines += [ "USE_EVDEV_GESTURES" ]
@@ -28,6 +30,11 @@
   }
 }
 
+copy("mojo_service_manager_policy") {
+  sources = [ "cros_healthd_private_policy.json" ]
+  outputs = [ "$root_out_dir/mojo_service_manager/{{source_file_part}}" ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [ "data_collector_unittest.cc" ]
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/cros_healthd_private_policy.json b/chromeos/ash/services/cros_healthd/private/cpp/cros_healthd_private_policy.json
new file mode 100644
index 0000000..365e46b
--- /dev/null
+++ b/chromeos/ash/services/cros_healthd/private/cpp/cros_healthd_private_policy.json
@@ -0,0 +1,11 @@
+[
+  {
+    // This is the selinux security context of ash chrome.
+    "identity": "u:r:cros_browser:s0",
+    "own": [ "ChromiumCrosHealthdDataCollector" ]
+  },
+  {
+    "identity": "u:r:cros_healthd:s0",
+    "request": [ "ChromiumCrosHealthdDataCollector" ]
+  }
+]
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
index 142df148..faf1499 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
+++ b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
@@ -12,8 +12,10 @@
 #include "base/notreached.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "chromeos/components/mojo_service_manager/connection.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "third_party/cros_system_api/mojo/service_constants.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
 
@@ -124,7 +126,13 @@
 
 };  // namespace
 
-DataCollector::DataCollector() : DataCollector(GetDataCollectorDelegate()) {}
+DataCollector::DataCollector() : DataCollector(GetDataCollectorDelegate()) {
+  if (chromeos::mojo_service_manager::IsServiceManagerBound()) {
+    chromeos::mojo_service_manager::GetServiceManagerProxy()->Register(
+        chromeos::mojo_services::kChromiumCrosHealthdDataCollector,
+        provider_receiver_.BindNewPipeAndPassRemote());
+  }
+}
 
 DataCollector::DataCollector(Delegate* delegate) : delegate_(delegate) {}
 
@@ -132,7 +140,9 @@
 
 mojo::PendingRemote<mojom::ChromiumDataCollector>
 DataCollector::BindNewPipeAndPassRemote() {
-  return receiver_.BindNewPipeAndPassRemote();
+  mojo::PendingRemote<mojom::ChromiumDataCollector> remote;
+  receiver_set_.Add(this, remote.InitWithNewPipeAndPassReceiver());
+  return remote;
 }
 
 void DataCollector::GetTouchscreenDevices(
@@ -148,6 +158,13 @@
   std::move(callback).Run(delegate_->GetTouchpadLibraryName());
 }
 
+void DataCollector::Request(
+    mojo_service_manager::mojom::ProcessIdentityPtr identity,
+    mojo::ScopedMessagePipeHandle receiver) {
+  receiver_set_.Add(this, mojo::PendingReceiver<mojom::ChromiumDataCollector>(
+                              std::move(receiver)));
+}
+
 }  // namespace internal
 }  // namespace cros_healthd
 }  // namespace chromeos
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h
index 8d68ffb3..3fac68a 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h
+++ b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h
@@ -6,13 +6,16 @@
 #define CHROMEOS_ASH_SERVICES_CROS_HEALTHD_PRIVATE_CPP_DATA_COLLECTOR_H_
 
 #include "chromeos/ash/services/cros_healthd/private/mojom/cros_healthd_internal.mojom.h"
+#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 
 namespace chromeos {
 namespace cros_healthd {
 namespace internal {
 
-class DataCollector : public mojom::ChromiumDataCollector {
+class DataCollector : public mojom::ChromiumDataCollector,
+                      public mojo_service_manager::mojom::ServiceProvider {
  public:
   // Delegate class to be replaced for testing.
   class Delegate {
@@ -38,10 +41,17 @@
   void GetTouchscreenDevices(GetTouchscreenDevicesCallback callback) override;
   void GetTouchpadLibraryName(GetTouchpadLibraryNameCallback callback) override;
 
+  // chromeos::mojo_service_manager::mojom::ServiceProvider overrides.
+  void Request(mojo_service_manager::mojom::ProcessIdentityPtr identity,
+               mojo::ScopedMessagePipeHandle receiver) override;
+
   // Pointer to the delegate.
   Delegate* const delegate_;
-  // The mojo receiver.
-  mojo::Receiver<mojom::ChromiumDataCollector> receiver_{this};
+  // The mojo receiver of service provider.
+  mojo::Receiver<chromeos::mojo_service_manager::mojom::ServiceProvider>
+      provider_receiver_{this};
+  // The mojo receiver set of data collector.
+  mojo::ReceiverSet<mojom::ChromiumDataCollector> receiver_set_;
 };
 
 }  // namespace internal
diff --git a/chromeos/services/libassistant/BUILD.gn b/chromeos/services/libassistant/BUILD.gn
index 2cfc027..b5bc1da 100644
--- a/chromeos/services/libassistant/BUILD.gn
+++ b/chromeos/services/libassistant/BUILD.gn
@@ -62,6 +62,7 @@
     "//base",
     "//chromeos/ash/components/dbus",
     "//chromeos/ash/services/assistant/public/cpp",
+    "//chromeos/assistant/internal:libassistant",
     "//chromeos/assistant/internal:libassistant_shared_headers",
     "//chromeos/dbus/dlcservice",
     "//chromeos/dbus/dlcservice:dlcservice_proto",
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index da72b5b..b722ee4 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -12,14 +12,6 @@
   # crbug.com/1184931
   "health.ProbeBatteryMetrics",
 
-  # crbug.com/1242415
-  "hwsec.AttestationEnrollOnly",
-  "hwsec.ChapsAttributePolicy",
-  "hwsec.ChapsPKCS1V15",
-  "policy.AccessibilityPolicies.sticky_keys",
-  "policy.AllowDinosaurEasterEgg",
-  "policy.DisableScreenshotsExtension",
-
   # crbug.com/1238654
   "lockscreen.KeyboardShortcut",
   "camera.EncodeAccelJPEG",
diff --git a/components/BUILD.gn b/components/BUILD.gn
index bb3b8452..ddc1bcef 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -91,6 +91,7 @@
 
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
@@ -967,6 +968,7 @@
       additional_manifest_fragments = [
         "//build/config/fuchsia/test/present_view.shard.test-cml",
         "//build/config/fuchsia/test/fonts.shard.test-cml",
+        "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
         "//build/config/fuchsia/test/network.shard.test-cml",
       ]
     }
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 43c4e11f..acc29385 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1675,7 +1675,7 @@
   // It is common for not-humanly-visible elements to have very small yet
   // positive bounds. The threshold of 10 pixels is chosen rather arbitrarily.
   constexpr int kMinPixelSize = 10;
-  gfx::Rect bounds = element.BoundsInViewport();
+  gfx::Rect bounds = element.BoundsInWidget();
   return element.IsFocusable() && bounds.width() > kMinPixelSize &&
          bounds.height() > kMinPixelSize;
 }
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index 0999f1ea..c359461 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -195,20 +195,20 @@
 // A heuristic visibility detection. See crbug.com/1335257 for an overview of
 // relevant aspects.
 //
-// Note that WebElement::BoundsInViewport(), WebElement::GetClientSize(), and
-// WebElement::GetScrollSize() include the padding but do not include the border
-// and margin. BoundsInViewport() additionally scales the dimensions according
-// to the zoom factor.
+// Note that WebElement::BoundsInWidget(), WebElement::GetClientSize(),
+// and WebElement::GetScrollSize() include the padding but do not include the
+// border and margin. BoundsInWidget() additionally scales the
+// dimensions according to the zoom factor.
 //
 // It seems that invisible fields on websites typically have dimensions between
 // 0 and 10 pixels, before the zoom factor. Therefore choosing `kMinPixelSize`
 // is easier without including the zoom factor. For that reason, this function
-// prefers GetClientSize() over BoundsInViewport().
+// prefers GetClientSize() over BoundsInWidget().
 //
 // This function does not check the position in the viewport because fields in
 // iframes commonly are visible despite the body having height zero. Therefore,
-// `e.GetDocument().Body().BoundsInViewport().Intersects(e.BoundsInViewport())`
-// yields false negatives.
+// `e.GetDocument().Body().BoundsInWidget().Intersects(
+//      e.BoundsInWidget())` yields false negatives.
 //
 // Exposed for testing purposes.
 //
diff --git a/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index 562d0d2e..81d32a3 100644
--- a/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -1020,7 +1020,7 @@
 
   auto RunTestCases = [](const std::vector<WebElement>& iframes) {
     for (WebElement iframe : iframes) {
-      gfx::Rect bounds = iframe.BoundsInViewport();
+      gfx::Rect bounds = iframe.BoundsInWidget();
       bool expectation = iframe.HasAttribute("data-visible");
       SCOPED_TRACE(
           testing::Message()
@@ -1139,7 +1139,7 @@
 
   auto RunTestCases = [](const std::vector<WebElement>& inputs) {
     for (WebElement input : inputs) {
-      gfx::Rect bounds = input.BoundsInViewport();
+      gfx::Rect bounds = input.BoundsInWidget();
       bool expectation = input.HasAttribute("data-visible");
       SCOPED_TRACE(
           testing::Message()
diff --git a/components/autofill/content/renderer/form_cache_browsertest.cc b/components/autofill/content/renderer/form_cache_browsertest.cc
index 8e6aa08..45754a94 100644
--- a/components/autofill/content/renderer/form_cache_browsertest.cc
+++ b/components/autofill/content/renderer/form_cache_browsertest.cc
@@ -305,7 +305,7 @@
   WebElement iframe3 = GetElementById(GetMainFrame()->GetDocument(), "frame3");
 
   auto GetSize = [](const WebElement& element) {
-    gfx::Rect bounds = element.BoundsInViewport();
+    gfx::Rect bounds = element.BoundsInWidget();
     return bounds.width() * bounds.height();
   };
 
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 97414a0a..7a4c3c2b 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -253,6 +253,8 @@
     "service/cup_factory.h",
     "service/cup_impl.cc",
     "service/cup_impl.h",
+    "service/offline_service.cc",
+    "service/offline_service.h",
     "service/rpc_type.h",
     "service/server_url_fetcher.cc",
     "service/server_url_fetcher.h",
@@ -563,6 +565,7 @@
     "service/mock_simple_url_loader_factory.h",
     "service/mock_url_loader.cc",
     "service/mock_url_loader.h",
+    "service/offline_service_unittests.cc",
     "service/server_url_fetcher_unittest.cc",
     "service/service_impl_unittest.cc",
     "service/service_request_sender_impl_unittest.cc",
diff --git a/components/autofill_assistant/browser/actions/js_flow_action.cc b/components/autofill_assistant/browser/actions/js_flow_action.cc
index 5f0825f..9866910 100644
--- a/components/autofill_assistant/browser/actions/js_flow_action.cc
+++ b/components/autofill_assistant/browser/actions/js_flow_action.cc
@@ -85,8 +85,9 @@
                             std::unique_ptr<base::Value> result_value)>
         finished_callback,
     std::unique_ptr<ProcessedActionProto> processed_action) {
-  VLOG(2) << "Native action finished with status "
-          << processed_action->status();
+  // Intentionally left empty to make output more readable in combination with
+  // the start message above.
+  VLOG(2) << "                       " << processed_action->status();
 
   current_native_action_.reset();
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 165075e..1ef4d86 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -268,6 +268,52 @@
   repeated MatchInfoProto match_info = 1;
 }
 
+message GetNoRoundtripScriptsByHashPrefixRequestProto {
+  // The number of leading bits to consider from hash_prefix. The remaining
+  // trailing bits from hash_prefix should be ignored.
+  optional uint32 hash_prefix_length = 1;
+
+  // The CityHash64 of the URL of interest. Only the first hash_prefix_length
+  // bits are meaningful.
+  optional uint64 hash_prefix = 2;
+
+  // The client context.
+  optional ClientContextProto client_context = 3;
+
+  // Optional script parameters. Note that only a subset of parameters are
+  // allowed to be sent from the client.
+  repeated ScriptParameterProto script_parameters = 4;
+}
+
+message GetNoRoundtripScriptsByHashPrefixResponseProto {
+  // CUP-signed version of this proto. When set, the rest of the fields in
+  // ScriptActionRequestProto should be ignored.
+  optional CUPResponseData cup_data = 1;
+
+  message MatchInfo {
+    message RoutineScript {
+      optional SupportedScriptProto routine = 1;
+      // The set of actions to execute. If the last executed action is not a
+      // stop action or a stop action inside a JS flow action the script will
+      // fail.
+      optional ActionsResponseProto autobot_response = 2;
+    }
+
+    // The domain for the script
+    optional string domain = 1;
+
+    // The response of the supports site request for this domain.
+    optional SupportsScriptResponseProto supports_site_response = 2;
+
+    // The scripts for the domain. Can contain one main script and several
+    // interrupt scripts.
+    repeated RoutineScript routine_scripts = 3;
+  }
+  // List of potential scripts matches for the request URL hash prefix. Callers
+  // should inspect each element of the list to find the exact match, if any.
+  repeated MatchInfo match_infos = 2;
+}
+
 // Request to get user data.
 message GetUserDataRequestProto {
   message PaymentMethodRequest {
diff --git a/components/autofill_assistant/browser/service/offline_service.cc b/components/autofill_assistant/browser/service/offline_service.cc
new file mode 100644
index 0000000..2f9484e
--- /dev/null
+++ b/components/autofill_assistant/browser/service/offline_service.cc
@@ -0,0 +1,132 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/offline_service.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/trigger_context.h"
+#include "net/http/http_status_code.h"
+
+namespace autofill_assistant {
+
+LocalScriptStore::LocalScriptStore(
+    std::vector<GetNoRoundtripScriptsByHashPrefixResponseProto::MatchInfo::
+                    RoutineScript> routines,
+    std::string domain,
+    SupportsScriptResponseProto supports_site_response)
+    : routines(routines),
+      domain(domain),
+      supports_site_response(supports_site_response) {}
+LocalScriptStore::LocalScriptStore(const LocalScriptStore&) = default;
+LocalScriptStore::LocalScriptStore(LocalScriptStore&&) = default;
+LocalScriptStore::~LocalScriptStore() = default;
+
+// static
+std::unique_ptr<OfflineService> OfflineService::Create(
+    const LocalScriptStore& script_store) {
+  return std::make_unique<OfflineService>(script_store);
+}
+
+OfflineService::OfflineService(const LocalScriptStore& script_store)
+    : script_store_(script_store) {}
+
+OfflineService::~OfflineService() = default;
+
+void OfflineService::SetScriptStoreConfig(
+    const ScriptStoreConfig& script_store_config) {
+  script_store_config_ = script_store_config;
+}
+
+void OfflineService::GetScriptsForUrl(
+    const GURL& url,
+    const TriggerContext& trigger_context,
+    ServiceRequestSender::ResponseCallback callback) {
+  const std::string supports_site_response_str =
+      script_store_.supports_site_response.SerializeAsString();
+
+  const auto response_info = ServiceRequestSender::ResponseInfo(
+      {.encoded_body_length = static_cast<int64_t>(
+           script_store_.supports_site_response.ByteSizeLong())});
+
+  const int status =
+      url.DomainIs(script_store_.domain) ? net::HTTP_OK : net::HTTP_BAD_REQUEST;
+
+  std::move(callback).Run(status, supports_site_response_str, response_info);
+}
+
+void OfflineService::GetActions(
+    const std::string& script_path,
+    const GURL& url,
+    const TriggerContext& trigger_context,
+    const std::string& global_payload,
+    const std::string& script_payload,
+    ServiceRequestSender::ResponseCallback callback) {
+  DCHECK(!script_path.empty());
+
+  ServiceRequestSender::ResponseInfo response_info;
+  response_info.encoded_body_length = 0;
+
+  for (const auto& routine : script_store_.routines) {
+    if (routine.routine().path() != script_path) {
+      continue;
+    }
+
+    std::string get_actions_response;
+    routine.autobot_response().SerializeToString(&get_actions_response);
+    response_info.encoded_body_length =
+        static_cast<int64_t>(routine.autobot_response().ByteSizeLong());
+    std::move(callback).Run(net::HTTP_OK, get_actions_response, response_info);
+    return;
+  }
+
+  std::move(callback).Run(net::HTTP_BAD_REQUEST, "", response_info);
+}
+
+void OfflineService::GetNextActions(
+    const TriggerContext& trigger_context,
+    const std::string& previous_global_payload,
+    const std::string& previous_script_payload,
+    const std::vector<ProcessedActionProto>& processed_actions,
+    const RoundtripTimingStats& timing_stats,
+    const RoundtripNetworkStats& network_stats,
+    ServiceRequestSender::ResponseCallback callback) {
+  VLOG(1) << __func__ << "called in OfflineService, returning empty list";
+  std::move(callback).Run(net::HTTP_OK, "",
+                          ServiceRequestSender::ResponseInfo());
+}
+
+void OfflineService::GetUserData(
+    const CollectUserDataOptions& options,
+    uint64_t run_id,
+    const UserData* user_data,
+    ServiceRequestSender::ResponseCallback callback) {
+  LOG(ERROR) << __func__ << "not available in OfflineService";
+  std::move(callback).Run(net::HTTP_METHOD_NOT_ALLOWED, "",
+                          ServiceRequestSender::ResponseInfo());
+}
+
+void OfflineService::SetDisableRpcSigning(bool disable_rpc_signing) {
+  LOG(WARNING) << __func__ << "not available in OfflineService";
+}
+
+void OfflineService::UpdateAnnotateDomModelContext(int64_t model_version) {
+  LOG(WARNING) << __func__ << "not available in OfflineService";
+}
+
+void OfflineService::UpdateJsFlowLibraryLoaded(
+    const bool js_flow_library_loaded) {
+  LOG(WARNING) << __func__ << "not available in OfflineService";
+}
+
+void OfflineService::ReportProgress(
+    const std::string& token,
+    const std::string& payload,
+    ServiceRequestSender::ResponseCallback callback) {}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/offline_service.h b/components/autofill_assistant/browser/service/offline_service.h
new file mode 100644
index 0000000..a697756
--- /dev/null
+++ b/components/autofill_assistant/browser/service/offline_service.h
@@ -0,0 +1,112 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_OFFLINE_SERVICE_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_OFFLINE_SERVICE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/service/service.h"
+#include "components/autofill_assistant/browser/service/service_request_sender.h"
+#include "components/autofill_assistant/browser/user_data.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+
+struct LocalScriptStore {
+  // Temporary boilerplate, this struct becomes a proper class in the child CL
+  ~LocalScriptStore();
+  LocalScriptStore(const LocalScriptStore&);
+  LocalScriptStore(LocalScriptStore&&);
+  LocalScriptStore& operator=(const LocalScriptStore&) = default;
+  LocalScriptStore& operator=(LocalScriptStore&&) = default;
+  LocalScriptStore(std::vector<GetNoRoundtripScriptsByHashPrefixResponseProto::
+                                   MatchInfo::RoutineScript> routines,
+                   std::string domain,
+                   SupportsScriptResponseProto supports_site_response);
+
+  // Contains pairs of [AutobotResponses, SupportedRoutine].
+  // TODO change SupportedRoutine to simply script_path
+  std::vector<
+      GetNoRoundtripScriptsByHashPrefixResponseProto::MatchInfo::RoutineScript>
+      routines;
+
+  // The domain that this LocalScriptStore is valid for
+  std::string domain;
+
+  // The results of SupportsScript for this domain/intent match
+  SupportsScriptResponseProto supports_site_response;
+};
+
+// An offline version of the service that fetches all actions at once and then
+// serves scripts without roundtrips.
+class OfflineService : public Service {
+ public:
+  // Convenience method for creating an offline service.
+  static std::unique_ptr<OfflineService> Create(
+      const LocalScriptStore& script_store);
+
+  explicit OfflineService(const LocalScriptStore& script_store);
+  OfflineService(const OfflineService&) = delete;
+  OfflineService& operator=(const OfflineService&) = delete;
+  ~OfflineService() override;
+
+  // Return the SupportsSite response from the local script store.
+  void GetScriptsForUrl(
+      const GURL& url,
+      const TriggerContext& trigger_context,
+      ServiceRequestSender::ResponseCallback callback) override;
+
+  // Return the action given a script_path from the local script store.
+  void GetActions(const std::string& script_path,
+                  const GURL& url,
+                  const TriggerContext& trigger_context,
+                  const std::string& global_payload,
+                  const std::string& script_payload,
+                  ServiceRequestSender::ResponseCallback callback) override;
+
+  // This call will not work with the local script store.
+  void GetNextActions(
+      const TriggerContext& trigger_context,
+      const std::string& previous_global_payload,
+      const std::string& previous_script_payload,
+      const std::vector<ProcessedActionProto>& processed_actions,
+      const RoundtripTimingStats& timing_stats,
+      const RoundtripNetworkStats& network_stats,
+      ServiceRequestSender::ResponseCallback callback) override;
+
+  // This call will return the script store config from the local script store.
+  void SetScriptStoreConfig(
+      const ScriptStoreConfig& script_store_config) override;
+
+  // This call will not work with the local script store.
+  void GetUserData(const CollectUserDataOptions& options,
+                   uint64_t run_id,
+                   const UserData* user_data,
+                   ServiceRequestSender::ResponseCallback callback) override;
+
+  // This call will not work with the local script store.
+  void SetDisableRpcSigning(bool disable_rpc_signing) override;
+
+  // This call will not work with the local script store.
+  void UpdateAnnotateDomModelContext(int64_t model_version) override;
+
+  // This call will not work with the local script store.
+  void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded) override;
+
+  // TODO(mruel): keeping the implementation of this function for a child CL so
+  // it can be reviewed separately.
+  void ReportProgress(const std::string& token,
+                      const std::string& payload,
+                      ServiceRequestSender::ResponseCallback callback) override;
+
+ private:
+  ScriptStoreConfig script_store_config_;
+  const LocalScriptStore script_store_;
+};
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_OFFLINE_SERVICE_H_
diff --git a/components/autofill_assistant/browser/service/offline_service_unittests.cc b/components/autofill_assistant/browser/service/offline_service_unittests.cc
new file mode 100644
index 0000000..05311854
--- /dev/null
+++ b/components/autofill_assistant/browser/service/offline_service_unittests.cc
@@ -0,0 +1,118 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "components/autofill_assistant/browser/service/offline_service.h"
+
+#include <memory>
+#include "components/autofill_assistant/browser/service/offline_service.h"
+
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SaveArg;
+
+namespace {
+const char kFakeUrl[] = "https://www.example.com";
+
+class OfflineServiceTest : public testing::Test {
+ protected:
+  ~OfflineServiceTest() override = default;
+
+  base::MockCallback<ServiceRequestSender::ResponseCallback>
+      mock_response_callback_;
+
+  SupportsScriptResponseProto supports_site_response_;
+  std::string domain_;
+  std::vector<
+      GetNoRoundtripScriptsByHashPrefixResponseProto::MatchInfo::RoutineScript>
+      routines_;
+
+  OfflineService GetService() {
+    return OfflineService(
+        LocalScriptStore(routines_, domain_, supports_site_response_));
+  }
+};
+
+TEST_F(OfflineServiceTest, WithRightUrlGetScriptsSucceeds) {
+  domain_ = "example.com";
+  supports_site_response_.add_scripts()->set_path("test_path");
+
+  EXPECT_CALL(
+      mock_response_callback_,
+      Run(net::HTTP_OK, supports_site_response_.SerializeAsString(), _));
+
+  GetService().GetScriptsForUrl(GURL(kFakeUrl), TriggerContext(),
+                                mock_response_callback_.Get());
+}
+
+TEST_F(OfflineServiceTest, WithWrongUrlGetScriptsFails) {
+  domain_ = "example.com";
+  supports_site_response_.add_scripts()->set_path("test_path");
+
+  EXPECT_CALL(mock_response_callback_,
+              Run(net::HTTP_BAD_REQUEST,
+                  std::string(supports_site_response_.SerializeAsString()), _));
+
+  GetService().GetScriptsForUrl(GURL("https://www.wrong.com"), TriggerContext(),
+                                mock_response_callback_.Get());
+}
+
+TEST_F(OfflineServiceTest, WithExistingPathGetActionsSucceeds) {
+  domain_ = "example.com";
+  routines_.resize(1);
+  routines_[0].mutable_routine()->set_path("script_path");
+  routines_[0].mutable_autobot_response()->set_run_id(0);
+
+  EXPECT_CALL(
+      mock_response_callback_,
+      Run(net::HTTP_OK,
+          std::string(routines_.at(0).autobot_response().SerializeAsString()),
+          _));
+
+  GetService().GetActions("script_path", GURL(kFakeUrl), TriggerContext(), "",
+                          "", mock_response_callback_.Get());
+}
+
+TEST_F(OfflineServiceTest, GetActionsFailsGivenWrongPath) {
+  domain_ = "example.com";
+  routines_.resize(1);
+  routines_[0].mutable_routine()->set_path("script_path");
+  routines_[0].mutable_autobot_response()->set_run_id(0);
+
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_BAD_REQUEST, "", _));
+
+  GetService().GetActions("non_existing_path", GURL(kFakeUrl), TriggerContext(),
+                          "", "", mock_response_callback_.Get());
+}
+
+TEST_F(OfflineServiceTest, GetNextActionsReturnsEmptyResponse) {
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "", _));
+
+  const std::vector<ProcessedActionProto> processed_actions;
+
+  GetService().GetNextActions(TriggerContext(), "", "", processed_actions,
+                              RoundtripTimingStats(), RoundtripNetworkStats(),
+                              mock_response_callback_.Get());
+}
+
+TEST_F(OfflineServiceTest, GetUserDataFails) {
+  EXPECT_CALL(mock_response_callback_,
+              Run(net::HTTP_METHOD_NOT_ALLOWED, "", _));
+  const UserData user_data;
+  GetService().GetUserData(CollectUserDataOptions(), 0, &user_data,
+                           mock_response_callback_.Get());
+}
+
+}  // namespace
+
+}  // namespace autofill_assistant
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index a4a1c079..3efcc8e 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -1590,6 +1590,9 @@
 DailyVisitsResult HistoryBackend::GetDailyVisitsToHost(const GURL& host,
                                                        base::Time begin_time,
                                                        base::Time end_time) {
+  if (!db_) {
+    return {};
+  }
   return db_->GetDailyVisitsToHost(host, begin_time, end_time);
 }
 
diff --git a/components/language/core/browser/url_language_histogram.cc b/components/language/core/browser/url_language_histogram.cc
index 7663776..898f4a64 100644
--- a/components/language/core/browser/url_language_histogram.cc
+++ b/components/language/core/browser/url_language_histogram.cc
@@ -25,9 +25,9 @@
 const float kDiscountFactor = 0.75f;
 
 // Gets the sum of the counter for all languages in the histogram.
-int GetCountersSum(const base::Value& dict) {
+int GetCountersSum(const base::Value::Dict& dict) {
   int sum = 0;
-  for (const auto itr : dict.DictItems()) {
+  for (const auto itr : dict) {
     if (itr.second.is_int())
       sum += itr.second.GetInt();
   }
@@ -35,10 +35,10 @@
 }
 
 // Removes languages with small counter values and discount remaining counters.
-void DiscountAndCleanCounters(base::Value* dict) {
+void DiscountAndCleanCounters(base::Value::Dict& dict) {
   std::set<std::string> remove_keys;
 
-  for (const auto itr : dict->DictItems()) {
+  for (const auto itr : dict) {
     // Remove languages with invalid or small values.
     if (!itr.second.is_int() ||
         itr.second.GetInt() < (kCutoffRatio * kMaxCountersSum)) {
@@ -47,16 +47,16 @@
     }
 
     // Discount the value.
-    dict->SetIntKey(itr.first, itr.second.GetInt() * kDiscountFactor);
+    dict.Set(itr.first, int(itr.second.GetInt() * kDiscountFactor));
   }
 
   for (const std::string& lang_to_remove : remove_keys)
-    dict->RemoveKey(lang_to_remove);
+    dict.Remove(lang_to_remove);
 }
 
 // Transforms the counters from prefs into a list of LanguageInfo structs.
 std::vector<UrlLanguageHistogram::LanguageInfo> GetAllLanguages(
-    const base::Value& dict) {
+    const base::Value::Dict& dict) {
   int counters_sum = GetCountersSum(dict);
 
   // If the sample is not large enough yet, pretend there are no top languages.
@@ -64,7 +64,7 @@
     return std::vector<UrlLanguageHistogram::LanguageInfo>();
 
   std::vector<UrlLanguageHistogram::LanguageInfo> top_languages;
-  for (const auto itr : dict.DictItems()) {
+  for (const auto itr : dict) {
     if (!itr.second.is_int())
       continue;
     top_languages.emplace_back(
@@ -90,7 +90,7 @@
 UrlLanguageHistogram::GetTopLanguages() const {
   std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
       GetAllLanguages(
-          *pref_service_->GetDictionary(kUrlLanguageHistogramCounters));
+          pref_service_->GetValueDict(kUrlLanguageHistogramCounters));
 
   std::sort(top_languages.begin(), top_languages.end(),
             [](UrlLanguageHistogram::LanguageInfo a,
@@ -103,27 +103,27 @@
 
 float UrlLanguageHistogram::GetLanguageFrequency(
     const std::string& language_code) const {
-  const base::Value* dict =
-      pref_service_->GetDictionary(kUrlLanguageHistogramCounters);
-  int counters_sum = GetCountersSum(*dict);
+  const base::Value::Dict& dict =
+      pref_service_->GetValueDict(kUrlLanguageHistogramCounters);
+  int counters_sum = GetCountersSum(dict);
   // If the sample is not large enough yet, pretend there are no top languages.
   if (counters_sum < kMinCountersSum)
     return 0;
 
   // If the key |language_code| does not exist, |counter_value| stays 0.
-  int counter_value = dict->FindIntKey(language_code).value_or(0);
+  int counter_value = dict.FindInt(language_code).value_or(0);
 
   return static_cast<float>(counter_value) / counters_sum;
 }
 
 void UrlLanguageHistogram::OnPageVisited(const std::string& language_code) {
   DictionaryPrefUpdate update(pref_service_, kUrlLanguageHistogramCounters);
-  base::Value* dict = update.Get();
+  base::Value::Dict& dict = update->GetDict();
   // If the key |language_code| does not exist, |counter_value| stays 0.
-  int counter_value = dict->FindIntKey(language_code).value_or(0);
-  dict->SetIntKey(language_code, counter_value + 1);
+  int counter_value = dict.FindInt(language_code).value_or(0);
+  dict.Set(language_code, counter_value + 1);
 
-  if (GetCountersSum(*dict) > kMaxCountersSum)
+  if (GetCountersSum(dict) > kMaxCountersSum)
     DiscountAndCleanCounters(dict);
 }
 
diff --git a/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.cc b/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.cc
index 744a63ec..8f6e9aa 100644
--- a/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.cc
+++ b/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.cc
@@ -51,6 +51,11 @@
   return prerendering_state_;
 }
 
+absl::optional<base::TimeDelta>
+FakePageLoadMetricsObserverDelegate::GetActivationStart() const {
+  return activation_start_;
+}
+
 const PageLoadMetricsObserverDelegate::BackForwardCacheRestore&
 FakePageLoadMetricsObserverDelegate::GetBackForwardCacheRestore(
     size_t index) const {
diff --git a/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.h b/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.h
index ee7d439..215d4e39 100644
--- a/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.h
+++ b/components/page_load_metrics/browser/fake_page_load_metrics_observer_delegate.h
@@ -37,6 +37,7 @@
   absl::optional<base::TimeDelta> GetTimeToFirstBackground() const override;
   absl::optional<base::TimeDelta> GetTimeToFirstForeground() const override;
   PrerenderingState GetPrerenderingState() const override;
+  absl::optional<base::TimeDelta> GetActivationStart() const override;
   // By default no BackForwardCacheRestores are present, tests can add them by
   // calling |AddBackForwardCacheRestore|.
   const BackForwardCacheRestore& GetBackForwardCacheRestore(
@@ -106,6 +107,7 @@
   bool started_in_foreground_ = true;
   PrerenderingState prerendering_state_ = PrerenderingState::kNoPrerendering;
   PageVisibility visibility_at_activation_ = PageVisibility::kNotInitialized;
+  absl::optional<base::TimeDelta> activation_start_ = absl::nullopt;
 
   // This vector backs the |GetBackForwardCacheRestore| and
   // |GetMostRecentBackForwardCacheRestore| methods.
diff --git a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
index c432853..34b6770 100644
--- a/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
+++ b/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
@@ -190,10 +190,10 @@
           WebFeature::kTextDetectorDetect,
           WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation,
           WebFeature::kV8HTMLVideoElement_GetVideoPlaybackQuality_Method,
-          WebFeature::kRTCPeerConnectionConstructedWithPlanB,
-          WebFeature::kRTCPeerConnectionConstructedWithUnifiedPlan,
-          WebFeature::kRTCPeerConnectionUsingComplexPlanB,
-          WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan,
+          WebFeature::kOBSOLETE_RTCPeerConnectionConstructedWithPlanB,
+          WebFeature::kOBSOLETE_RTCPeerConnectionConstructedWithUnifiedPlan,
+          WebFeature::kOBSOLETE_RTCPeerConnectionUsingComplexPlanB,
+          WebFeature::kOBSOLETE_RTCPeerConnectionUsingComplexUnifiedPlan,
           WebFeature::kWebPImage,
           WebFeature::kAVIFImage,
           WebFeature::kGetDisplayMedia,
diff --git a/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h b/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
index f900ade..f96d098ee 100644
--- a/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
+++ b/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
@@ -121,6 +121,8 @@
   virtual PrerenderingState GetPrerenderingState() const = 0;
   // True iff the page is prerendered and activation_start is not yet arrived.
   bool IsInPrerenderingBeforeActivationStart() const;
+  // Returns activation start if activation start was arrived, or nullopt.
+  virtual absl::optional<base::TimeDelta> GetActivationStart() const = 0;
 
   // Whether the page load was initiated by a user.
   virtual const UserInitiatedInfo& GetUserInitiatedInfo() const = 0;
diff --git a/components/page_load_metrics/browser/page_load_metrics_util.cc b/components/page_load_metrics/browser/page_load_metrics_util.cc
index 7cc66d1..18ce7929 100644
--- a/components/page_load_metrics/browser/page_load_metrics_util.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_util.cc
@@ -159,8 +159,7 @@
 }
 
 absl::optional<base::TimeDelta> GetNonPrerenderingBackgroundStartTiming(
-    const PageLoadMetricsObserverDelegate& delegate,
-    const page_load_metrics::mojom::PageLoadTiming& timing) {
+    const PageLoadMetricsObserverDelegate& delegate) {
   switch (delegate.GetPrerenderingState()) {
     case PrerenderingState::kNoPrerendering:
       if (delegate.StartedInForeground()) {
@@ -175,26 +174,34 @@
       if (delegate.GetVisibilityAtActivation() == PageVisibility::kForeground) {
         return delegate.GetTimeToFirstBackground();
       } else {
-        return timing.activation_start;
+        return delegate.GetActivationStart();
       }
   }
 }
 
 bool EventOccurredBeforeNonPrerenderingBackgroundStart(
     const PageLoadMetricsObserverDelegate& delegate,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
     const base::TimeDelta& event) {
   // If background start is nullopt, it'll must be greater than already
   // occurred event.
   const base::TimeDelta bg_start =
-      GetNonPrerenderingBackgroundStartTiming(delegate, timing)
-          .value_or(base::TimeDelta::Max());
+      GetNonPrerenderingBackgroundStartTiming(delegate).value_or(
+          base::TimeDelta::Max());
   return event < bg_start;
 }
 
+// Currently, multiple implementations of PageLoadMetricsObserver is ongoing.
+// We'll left the old version for a while.
+// TODO(https://crbug.com/1317494): Use the above version and delete this.
+bool EventOccurredBeforeNonPrerenderingBackgroundStart(
+    const PageLoadMetricsObserverDelegate& delegate,
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const base::TimeDelta& event) {
+  return EventOccurredBeforeNonPrerenderingBackgroundStart(delegate, event);
+}
+
 base::TimeDelta CorrectEventAsNavigationOrActivationOrigined(
     const PageLoadMetricsObserverDelegate& delegate,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
     const base::TimeDelta& event) {
   base::TimeDelta zero = base::Seconds(0);
 
@@ -205,15 +212,22 @@
     case PrerenderingState::kActivatedNoActivationStart:
       return zero;
     case PrerenderingState::kActivated: {
-      absl::optional<base::TimeDelta> origin = timing.activation_start;
-      DCHECK(origin.has_value());
-
-      base::TimeDelta corrected = event - origin.value();
-      return corrected < zero ? zero : corrected;
+      base::TimeDelta corrected = event - delegate.GetActivationStart().value();
+      return std::max(zero, corrected);
     }
   }
 }
 
+// Currently, multiple implementations of PageLoadMetricsObserver is ongoing.
+// We'll left the old version for a while.
+// TODO(https://crbug.com/1317494): Use the above version and delete this.
+base::TimeDelta CorrectEventAsNavigationOrActivationOrigined(
+    const PageLoadMetricsObserverDelegate& delegate,
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const base::TimeDelta& event) {
+  return CorrectEventAsNavigationOrActivationOrigined(delegate, event);
+}
+
 PageAbortInfo GetPageAbortInfo(
     const PageLoadMetricsObserverDelegate& delegate) {
   if (IsBackgroundAbort(delegate)) {
diff --git a/components/page_load_metrics/browser/page_load_metrics_util.h b/components/page_load_metrics/browser/page_load_metrics_util.h
index ff55b51..bfc2bb93 100644
--- a/components/page_load_metrics/browser/page_load_metrics_util.h
+++ b/components/page_load_metrics/browser/page_load_metrics_util.h
@@ -157,8 +157,7 @@
 // Note that this can be different from the return value of
 // `PageLoadMetricsObserverDelegate::GetTimeToFirstBackground`.
 absl::optional<base::TimeDelta> GetNonPrerenderingBackgroundStartTiming(
-    const PageLoadMetricsObserverDelegate& delegate,
-    const page_load_metrics::mojom::PageLoadTiming& timing);
+    const PageLoadMetricsObserverDelegate& delegate);
 
 // Returns true iff event occurred in prerendered before activation or before
 // background start.
@@ -167,6 +166,9 @@
 // In this case, arbitrary value will be returned.
 bool EventOccurredBeforeNonPrerenderingBackgroundStart(
     const PageLoadMetricsObserverDelegate& delegate,
+    const base::TimeDelta& event);
+bool EventOccurredBeforeNonPrerenderingBackgroundStart(
+    const PageLoadMetricsObserverDelegate& delegate,
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const base::TimeDelta& event);
 
@@ -178,6 +180,9 @@
 // are truncated as zero.
 base::TimeDelta CorrectEventAsNavigationOrActivationOrigined(
     const PageLoadMetricsObserverDelegate& delegate,
+    const base::TimeDelta& event);
+base::TimeDelta CorrectEventAsNavigationOrActivationOrigined(
+    const PageLoadMetricsObserverDelegate& delegate,
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const base::TimeDelta& event);
 
diff --git a/components/page_load_metrics/browser/page_load_metrics_util_unittest.cc b/components/page_load_metrics/browser/page_load_metrics_util_unittest.cc
index 2bc5d48d..b170969 100644
--- a/components/page_load_metrics/browser/page_load_metrics_util_unittest.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_util_unittest.cc
@@ -294,6 +294,7 @@
   for (const auto& test_case : test_cases) {
     page_load_metrics::FakePageLoadMetricsObserverDelegate delegate;
     delegate.prerendering_state_ = test_case.prerendering_state;
+    delegate.activation_start_ = test_case.activation_start;
     if (test_case.time_to_first_background.has_value()) {
       delegate.first_background_time_ =
           delegate.navigation_start_ +
@@ -327,13 +328,8 @@
         break;
     }
 
-    page_load_metrics::mojom::PageLoadTiming timing;
-    page_load_metrics::InitPageLoadTimingForTest(&timing);
-    timing.navigation_start = base::Time::FromDoubleT(1);
-    timing.activation_start = test_case.activation_start;
-
     absl::optional<base::TimeDelta> got =
-        GetNonPrerenderingBackgroundStartTiming(delegate, timing);
+        GetNonPrerenderingBackgroundStartTiming(delegate);
     EXPECT_EQ(test_case.expected_result, got);
   }
 }
@@ -364,15 +360,31 @@
   for (const auto& test_case : test_cases) {
     page_load_metrics::FakePageLoadMetricsObserverDelegate delegate;
     delegate.prerendering_state_ = test_case.prerendering_state;
+    delegate.activation_start_ = test_case.activation_start;
+
+    base::TimeDelta got =
+        CorrectEventAsNavigationOrActivationOrigined(delegate, test_case.event);
+    EXPECT_EQ(test_case.expected_result, got);
+
+    // Currently, multiple implementations of PageLoadMetricsObserver is
+    // ongoing. We'll left the old version for a while.
+    // TODO(https://crbug.com/1317494): Delete below.
 
     page_load_metrics::mojom::PageLoadTiming timing;
     page_load_metrics::InitPageLoadTimingForTest(&timing);
     timing.navigation_start = base::Time::FromDoubleT(1);
     timing.activation_start = test_case.activation_start;
 
-    base::TimeDelta got = CorrectEventAsNavigationOrActivationOrigined(
+    base::TimeDelta got2 = CorrectEventAsNavigationOrActivationOrigined(
         delegate, timing, test_case.event);
-    EXPECT_EQ(test_case.expected_result, got);
+    EXPECT_EQ(test_case.expected_result, got2);
+
+    // In some path, this function is called with old PageLoadTiming, which can
+    // lack activation_start. The result is the same for such case.
+    timing.activation_start = absl::nullopt;
+    base::TimeDelta got3 = CorrectEventAsNavigationOrActivationOrigined(
+        delegate, timing, test_case.event);
+    EXPECT_EQ(test_case.expected_result, got3);
   }
 }
 
diff --git a/components/page_load_metrics/browser/page_load_tracker.cc b/components/page_load_metrics/browser/page_load_tracker.cc
index 83f2758..a966a52 100644
--- a/components/page_load_metrics/browser/page_load_tracker.cc
+++ b/components/page_load_metrics/browser/page_load_tracker.cc
@@ -810,6 +810,7 @@
     DCHECK(prerendering_state_ ==
            PrerenderingState::kActivatedNoActivationStart);
     prerendering_state_ = PrerenderingState::kActivated;
+    activation_start_ = new_timing.activation_start;
   }
 
   const mojom::PaintTimingPtr& paint_timing =
@@ -1015,6 +1016,10 @@
   return prerendering_state_;
 }
 
+absl::optional<base::TimeDelta> PageLoadTracker::GetActivationStart() const {
+  return activation_start_;
+}
+
 const UserInitiatedInfo& PageLoadTracker::GetUserInitiatedInfo() const {
   return user_initiated_info_;
 }
diff --git a/components/page_load_metrics/browser/page_load_tracker.h b/components/page_load_metrics/browser/page_load_tracker.h
index dd2e2d0e..c1d139e 100644
--- a/components/page_load_metrics/browser/page_load_tracker.h
+++ b/components/page_load_metrics/browser/page_load_tracker.h
@@ -239,6 +239,7 @@
   absl::optional<base::TimeDelta> GetTimeToFirstBackground() const override;
   absl::optional<base::TimeDelta> GetTimeToFirstForeground() const override;
   PrerenderingState GetPrerenderingState() const override;
+  absl::optional<base::TimeDelta> GetActivationStart() const override;
   const BackForwardCacheRestore& GetBackForwardCacheRestore(
       size_t index) const override;
   bool StartedInForeground() const override;
@@ -511,6 +512,7 @@
   PrerenderingState prerendering_state_ = PrerenderingState::kNoPrerendering;
   // Holds the page's visibility at activation.
   PageVisibility visibility_at_activation_ = PageVisibility::kNotInitialized;
+  absl::optional<base::TimeDelta> activation_start_ = absl::nullopt;
 
   mojom::PageLoadTimingPtr last_dispatched_merged_page_timing_;
   blink::MobileFriendliness latest_mobile_friendliness_;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 96b2b73..32b8822 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -22539,6 +22539,7 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'deprecated': True,
       'default': True,
       'example_value': True,
       'id': 727,
@@ -32549,6 +32550,7 @@
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:104-106', 'chrome.*:104-106', 'android:104-106'],
+      'deprecated': True,
       'future_on': ['fuchsia'],
       'features': {
         'per_profile': True,
diff --git a/components/stylus_handwriting/android/BUILD.gn b/components/stylus_handwriting/android/BUILD.gn
index feedb39..dbb1b44 100644
--- a/components/stylus_handwriting/android/BUILD.gn
+++ b/components/stylus_handwriting/android/BUILD.gn
@@ -56,6 +56,7 @@
     "//base:base_junit_test_support",
     "//content/public/android:content_full_java",
     "//third_party/android_deps:com_android_support_support_annotations_java",
+    "//third_party/blink/public:blink_headers_java",
     "//third_party/junit:junit",
     "//third_party/mockito:mockito_java",
     "//ui/android:ui_no_recycler_view_java",
diff --git a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallback.java b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallback.java
index d9483ec..19a56c5 100644
--- a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallback.java
+++ b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallback.java
@@ -32,7 +32,7 @@
  */
 class DirectWritingServiceCallback
         extends android.widget.directwriting.IDirectWritingServiceCallback.Stub {
-    public static final String BUNDLE_KEY_SHOW_KEYBOARD = "showKeyboard";
+    static final String BUNDLE_KEY_SHOW_KEYBOARD = "showKeyboard";
     private static final String TAG = "DWCallbackImpl";
 
     // The following GESTURE_ and ACTION_ constants are defined as per the bundle data sent by the
diff --git a/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallbackTest.java b/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallbackTest.java
index 8be84fd..477f5ad 100644
--- a/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallbackTest.java
+++ b/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/DirectWritingServiceCallbackTest.java
@@ -6,17 +6,41 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
 
+import android.content.Context;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
+import org.chromium.blink_public.web.WebTextInputFlags;
+import org.chromium.blink_public.web.WebTextInputMode;
+import org.chromium.content.browser.input.ImeUtils;
+import org.chromium.content_public.browser.StylusWritingImeCallback;
+import org.chromium.ui.base.ime.TextInputAction;
+import org.chromium.ui.base.ime.TextInputType;
 
 /**
  * Unit tests for {@link DirectWritingServiceCallback}.
@@ -26,7 +50,21 @@
 public class DirectWritingServiceCallbackTest {
     private static final String SAMPLE_INPUT = "sample input";
 
+    @Mock
+    private StylusWritingImeCallback mImeCallback;
+    @Mock
+    private ViewGroup mContainerView;
+
     private DirectWritingServiceCallback mDwServiceCallback = new DirectWritingServiceCallback();
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        doReturn(mContainerView).when(mImeCallback).getContainerView();
+        doReturn(mContext).when(mContainerView).getContext();
+    }
 
     @Test
     @Feature({"Stylus Handwriting"})
@@ -59,4 +97,98 @@
         assertEquals(editBounds.top, mDwServiceCallback.getTop());
         assertEquals(editBounds.bottom, mDwServiceCallback.getBottom());
     }
-}
\ No newline at end of file
+
+    @Test
+    @Feature({"Stylus Handwriting"})
+    public void testHideKeyboardMessage() {
+        // hide keyboard is called only after Ime callback is set.
+        mDwServiceCallback.semForceHideSoftInput();
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback, never()).hideKeyboard();
+
+        mDwServiceCallback.setImeCallback(mImeCallback);
+        mDwServiceCallback.semForceHideSoftInput();
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback).hideKeyboard();
+    }
+
+    @Test
+    @Feature({"Stylus Handwriting"})
+    public void testShowKeyboardMessage() {
+        String action = "show keyboard";
+        // hide keyboard is called only after Ime callback is set.
+        Bundle bundle = spy(new Bundle());
+        bundle.putBoolean(DirectWritingServiceCallback.BUNDLE_KEY_SHOW_KEYBOARD, false);
+        mDwServiceCallback.onAppPrivateCommand(action, bundle);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback, never()).getContainerView();
+        verify(mImeCallback, never()).showSoftKeyboard();
+
+        mDwServiceCallback.setImeCallback(mImeCallback);
+        mDwServiceCallback.onAppPrivateCommand(action, bundle);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback).getContainerView();
+        verify(bundle).getBoolean(DirectWritingServiceCallback.BUNDLE_KEY_SHOW_KEYBOARD);
+        verify(mImeCallback, never()).showSoftKeyboard();
+
+        bundle.putBoolean(DirectWritingServiceCallback.BUNDLE_KEY_SHOW_KEYBOARD, true);
+        mDwServiceCallback.onAppPrivateCommand(action, bundle);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback).showSoftKeyboard();
+    }
+
+    @Test
+    @Feature({"Stylus Handwriting"})
+    public void testOnEditorActionMessage() {
+        int editorAction = EditorInfo.IME_ACTION_SEARCH;
+        // Editor action can be done only after Ime callback is set.
+        mDwServiceCallback.onEditorAction(editorAction);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback, never()).performEditorAction(anyInt());
+
+        mDwServiceCallback.setImeCallback(mImeCallback);
+        mDwServiceCallback.onEditorAction(editorAction);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback).performEditorAction(editorAction);
+    }
+
+    @Test
+    @Feature({"Stylus Handwriting"})
+    public void testSetTextSelectionMessage() {
+        // Text received from service is committed only after Ime callback is set.
+        int index = SAMPLE_INPUT.length();
+        mDwServiceCallback.setTextSelection(SAMPLE_INPUT, index);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback, never()).setEditableSelectionOffsets(anyInt(), anyInt());
+        verify(mImeCallback, never()).sendCompositionToNative(any(), anyInt(), anyBoolean());
+
+        // Text received from service replaces the current text in input.
+        mDwServiceCallback.setImeCallback(mImeCallback);
+        String currentInputText = "test";
+        mDwServiceCallback.updateInputState(currentInputText, 4, 4);
+        mDwServiceCallback.setTextSelection(SAMPLE_INPUT, index);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mImeCallback).setEditableSelectionOffsets(0, currentInputText.length());
+        verify(mImeCallback).sendCompositionToNative(SAMPLE_INPUT, index, true);
+        verify(mImeCallback).setEditableSelectionOffsets(index, index);
+    }
+
+    @Test
+    @Feature({"Stylus Handwriting"})
+    public void testEditorInfoAttributes() {
+        // Service callback returns default values until Editor info is set.
+        assertTrue(TextUtils.isEmpty(mDwServiceCallback.getPrivateImeOptions()));
+        assertEquals(0, mDwServiceCallback.getImeOptions());
+        assertEquals(0, mDwServiceCallback.getInputType());
+
+        EditorInfo editorInfo = new EditorInfo();
+        int index = SAMPLE_INPUT.length();
+        ImeUtils.computeEditorInfo(TextInputType.TEXT, WebTextInputFlags.NONE,
+                WebTextInputMode.DEFAULT, TextInputAction.SEARCH, index, index, SAMPLE_INPUT,
+                editorInfo);
+        mDwServiceCallback.updateEditorInfo(editorInfo);
+        assertEquals(editorInfo.privateImeOptions, mDwServiceCallback.getPrivateImeOptions());
+        assertEquals(editorInfo.imeOptions, mDwServiceCallback.getImeOptions());
+        assertEquals(editorInfo.inputType, mDwServiceCallback.getInputType());
+    }
+}
diff --git a/components/sync/engine/loopback_server/loopback_server.cc b/components/sync/engine/loopback_server/loopback_server.cc
index af8f045..0e39417c 100644
--- a/components/sync/engine/loopback_server/loopback_server.cc
+++ b/components/sync/engine/loopback_server/loopback_server.cc
@@ -679,17 +679,37 @@
     DCHECK(iter != entities_.end());
     committed_model_types.Put(iter->second->GetModelType());
 
-    // Notify observers about history having been synced. "History" sync is
-    // guarded by the user's selection in the settings page. This also excludes
-    // custom passphrase users who, in addition to HISTORY_DELETE_DIRECTIVES not
-    // being enabled, will commit encrypted specifics and hence cannot be
-    // iterated over.
-    if (observer_for_tests_ && iter->second->GetModelType() == SESSIONS &&
-        enabled_types.Has(HISTORY_DELETE_DIRECTIVES) &&
-        enabled_types.Has(TYPED_URLS)) {
-      for (const sync_pb::TabNavigation& navigation :
-           client_entity.specifics().session().tab().navigation()) {
-        observer_for_tests_->OnHistoryCommit(navigation.virtual_url());
+    // Notify observers about history having been synced. There are two
+    // iterations of "History sync" both guarded by the user's selection in the
+    // settings page:
+    // 1) The "old" one based on SESSIONS data, only enabled if TYPED_URLS and
+    //    HISTORY_DELETE_DIRECTIVES are also enabled. Note that for custom
+    //    passphrase users, HISTORY_DELETE_DIRECTIVES will not be enabled (and
+    //    since they commit encrypted specifics, the server couldn't inspect the
+    //    data anyway).
+    // 2) The "new" one based on a dedicated HISTORY data type. This data type
+    //    is itself disabled for custom passphrase users.
+    // In practice, at most one of TYPED_URLS or HISTORY can be enabled at the
+    // same time, so OnHistoryCommit() gets called at most once per URL.
+    DCHECK(!(enabled_types.Has(TYPED_URLS) && enabled_types.Has(HISTORY)));
+    if (observer_for_tests_) {
+      if (iter->second->GetModelType() == SESSIONS &&
+          enabled_types.Has(HISTORY_DELETE_DIRECTIVES) &&
+          enabled_types.Has(TYPED_URLS)) {
+        // "Old" history sync.
+        for (const sync_pb::TabNavigation& navigation :
+             client_entity.specifics().session().tab().navigation()) {
+          observer_for_tests_->OnHistoryCommit(navigation.virtual_url());
+        }
+      } else if (iter->second->GetModelType() == HISTORY) {
+        // "New" history sync.
+        const sync_pb::HistorySpecifics& specifics =
+            client_entity.specifics().history();
+        // The last entry of the redirect chain is the "actual" URL. In the case
+        // of no redirects, the "chain" has only a single entry.
+        observer_for_tests_->OnHistoryCommit(
+            specifics.redirect_entries(specifics.redirect_entries_size() - 1)
+                .url());
       }
     }
   }
diff --git a/components/sync/engine/loopback_server/loopback_server.h b/components/sync/engine/loopback_server/loopback_server.h
index 7c6b400..4636778 100644
--- a/components/sync/engine/loopback_server/loopback_server.h
+++ b/components/sync/engine/loopback_server/loopback_server.h
@@ -48,9 +48,11 @@
     virtual void OnCommit(const std::string& committer_invalidator_client_id,
                           syncer::ModelTypeSet committed_model_types) = 0;
 
-    // Called when a page URL is committed via SESSIONS and the user has
-    // enabled "history sync" in the settings UI (which is detected by verifying
-    // if TYPED_URLS is an enabled type, as part of the commit request).
+    // Called when a page URL is committed to server-side history. This can
+    // happen either via the HISTORY data type, or (while HISTORY is not yet
+    // rolled out) via SESSIONS when the user has enabled "history sync" in the
+    // settings UI (which is detected by verifying if TYPED_URLS is an enabled
+    // type, as part of the commit request).
     virtual void OnHistoryCommit(const std::string& url) = 0;
   };
 
diff --git a/components/sync/test/fake_server.h b/components/sync/test/fake_server.h
index c656e89a..aa4c5b0 100644
--- a/components/sync/test/fake_server.h
+++ b/components/sync/test/fake_server.h
@@ -250,6 +250,9 @@
                 syncer::ModelTypeSet committed_model_types) override;
   void OnHistoryCommit(const std::string& url) override;
 
+  // Returns all URLs that were committed to server-side history, which happens
+  // either through SESSIONS (if the "History" toggle is enabled) or through
+  // HISTORY.
   const std::set<std::string>& GetCommittedHistoryURLs() const;
 
   std::string GetStoreBirthday() const;
@@ -295,7 +298,8 @@
   // If set, the server will return HTTP errors.
   absl::optional<net::HttpStatusCode> http_error_status_code_;
 
-  // All URLs received via history sync (powered by SESSIONS).
+  // All URLs received via history sync (powered either by SESSIONS or by
+  // HISTORY).
   std::set<std::string> committed_history_urls_;
 
   // Used as the error_code field of ClientToServerResponse on all commit
diff --git a/components/viz/BUILD.gn b/components/viz/BUILD.gn
index a1d5c68..fb8f1cb 100644
--- a/components/viz/BUILD.gn
+++ b/components/viz/BUILD.gn
@@ -47,8 +47,10 @@
     # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests.
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
 
-    additional_manifest_fragments =
-        [ "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml" ]
+    additional_manifest_fragments = [
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
+      "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
+    ]
   }
 }
 
diff --git a/content/browser/file_system_access/fake_file_system_access_permission_context.cc b/content/browser/file_system_access/fake_file_system_access_permission_context.cc
index 88915e8..9fd9bff7 100644
--- a/content/browser/file_system_access/fake_file_system_access_permission_context.cc
+++ b/content/browser/file_system_access/fake_file_system_access_permission_context.cc
@@ -89,7 +89,8 @@
 }
 
 base::FilePath FakeFileSystemAccessPermissionContext::GetWellKnownDirectoryPath(
-    blink::mojom::WellKnownDirectory directory) {
+    blink::mojom::WellKnownDirectory directory,
+    const url::Origin& origin) {
   return well_known_directory_map_.find(directory) !=
                  well_known_directory_map_.end()
              ? well_known_directory_map_[directory]
diff --git a/content/browser/file_system_access/fake_file_system_access_permission_context.h b/content/browser/file_system_access/fake_file_system_access_permission_context.h
index fab876d..4c46108d 100644
--- a/content/browser/file_system_access/fake_file_system_access_permission_context.h
+++ b/content/browser/file_system_access/fake_file_system_access_permission_context.h
@@ -68,7 +68,8 @@
   // Retrieves a path which was earlier specified via SetWellKnownDirectoryPath.
   // Otherwise, returns an empty path.
   base::FilePath GetWellKnownDirectoryPath(
-      blink::mojom::WellKnownDirectory directory) override;
+      blink::mojom::WellKnownDirectory directory,
+      const url::Origin& origin) override;
 
   // Returns `kPickerTitle`.
   std::u16string GetPickerTitle(
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
index 63d8cd1..4ccc12b 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -487,7 +487,8 @@
         // Prioritize an explicitly stated well-known directory over an
         // implicitly remembered LastPicked directory.
         path_info.path = permission_context_->GetWellKnownDirectoryPath(
-            common_options->well_known_starting_directory);
+            common_options->well_known_starting_directory,
+            context.storage_key.origin());
       } else { /*well_known_starting_directory ==
                   blink::mojom::WellKnownDirectory::kDefault*/
         // If `id` empty or unset, fall back to the default LastPickedDirectory.
@@ -528,7 +529,8 @@
 
   if (!default_directory_exists && permission_context_) {
     default_directory = permission_context_->GetWellKnownDirectoryPath(
-        blink::mojom::WellKnownDirectory::kDefault);
+        blink::mojom::WellKnownDirectory::kDefault,
+        context.storage_key.origin());
   }
 
   auto request_directory_write_access =
diff --git a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
index 489881b..9e82b43 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
@@ -1295,7 +1295,8 @@
 
   EXPECT_CALL(
       permission_context_,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault,
+                                kTestStorageKey.origin()))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context_,
               GetLastPickedDirectory(kTestStorageKey.origin(), std::string()))
@@ -1381,7 +1382,8 @@
 
   EXPECT_CALL(
       permission_context_,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault,
+                                kTestStorageKey.origin()))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context_,
               GetLastPickedDirectory(kTestStorageKey.origin(), std::string()))
@@ -1463,7 +1465,8 @@
 
   EXPECT_CALL(
       permission_context_,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault,
+                                kTestStorageKey.origin()))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context_,
               GetLastPickedDirectory(kTestStorageKey.origin(), std::string()))
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
index 8e78765..d0e728ac 100644
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
@@ -497,9 +497,9 @@
   EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
@@ -578,9 +578,9 @@
   EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
@@ -666,9 +666,9 @@
   EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
@@ -762,9 +762,9 @@
   EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
@@ -828,9 +828,9 @@
   EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
@@ -1058,9 +1058,9 @@
   base::FilePath default_dir;
   default_dir = temp_dir_.GetPath().AppendASCII("default");
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(default_dir));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(bad_dir_info));
@@ -1240,9 +1240,9 @@
   base::FilePath default_dir;
   default_dir = temp_dir_.GetPath().AppendASCII("default");
 
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDefault, origin))
       .WillOnce(testing::Return(default_dir));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(bad_dir_info));
@@ -1336,9 +1336,9 @@
 
   // Well-known starting directory specified, so do not call
   // GetLastPickedDirectory.
-  EXPECT_CALL(
-      permission_context,
-      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDirDesktop))
+  EXPECT_CALL(permission_context,
+              GetWellKnownDirectoryPath(
+                  blink::mojom::WellKnownDirectory::kDirDesktop, origin))
       .WillOnce(testing::Return(desktop_dir));
   EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
       .WillOnce(testing::Return(std::u16string()));
diff --git a/content/browser/file_system_access/mock_file_system_access_permission_context.h b/content/browser/file_system_access/mock_file_system_access_permission_context.h
index b9be495..5ee6113 100644
--- a/content/browser/file_system_access/mock_file_system_access_permission_context.h
+++ b/content/browser/file_system_access/mock_file_system_access_permission_context.h
@@ -87,7 +87,8 @@
 
   MOCK_METHOD(base::FilePath,
               GetWellKnownDirectoryPath,
-              (blink::mojom::WellKnownDirectory directory),
+              (blink::mojom::WellKnownDirectory directory,
+               const url::Origin& origin),
               (override));
 
   MOCK_METHOD(std::u16string,
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index 990199e..8682e7d1 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -46,6 +46,8 @@
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
 #include "content/services/auction_worklet/worklet_devtools_debug_test_util.h"
 #include "content/services/auction_worklet/worklet_test_util.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/system/functions.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -874,11 +876,12 @@
       auction_worklet::mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
       base::Time auction_start_time,
       uint64_t trace_id,
-      GenerateBidCallback generate_bid_callback) override {
+      mojo::PendingAssociatedRemote<auction_worklet::mojom::GenerateBidClient>
+          generate_bid_client) override {
     generate_bid_called_ = true;
     // While the real BidderWorklet implementation supports multiple pending
     // callbacks, this class does not.
-    DCHECK(!generate_bid_callback_);
+    DCHECK(!generate_bid_client_);
 
     // per_buyer_timeout passed to GenerateBid() should not be empty, because
     // auction_config's all_buyers_timeout (which is the key of '*' in
@@ -901,7 +904,7 @@
     // before invoking SendPendingSignalsRequests().
     EXPECT_FALSE(send_pending_signals_requests_called_);
 
-    generate_bid_callback_ = std::move(generate_bid_callback);
+    generate_bid_client_.Bind(std::move(generate_bid_client));
     if (generate_bid_run_loop_)
       generate_bid_run_loop_->Quit();
   }
@@ -942,11 +945,11 @@
   }
 
   void WaitForGenerateBid() {
-    if (!generate_bid_callback_) {
+    if (!generate_bid_client_) {
       generate_bid_run_loop_ = std::make_unique<base::RunLoop>();
       generate_bid_run_loop_->Run();
       generate_bid_run_loop_.reset();
-      DCHECK(generate_bid_callback_);
+      DCHECK(generate_bid_client_);
     }
   }
 
@@ -965,33 +968,32 @@
     WaitForGenerateBid();
 
     if (!bid.has_value()) {
-      std::move(generate_bid_callback_)
-          .Run(/*bid=*/nullptr,
-               /*bidding_signals_data_version=*/0,
-               /*has_bidding_signals_data_version=*/false,
-               debug_loss_report_url,
-               /*debug_win_report_url=*/absl::nullopt,
-               /*set_priority=*/0,
-               /*has_set_priority=*/false,
-               /*pa_requests=*/std::move(pa_requests),
-               /*errors=*/std::vector<std::string>());
+      generate_bid_client_->OnGenerateBidComplete(
+          /*bid=*/nullptr,
+          /*bidding_signals_data_version=*/0,
+          /*has_bidding_signals_data_version=*/false, debug_loss_report_url,
+          /*debug_win_report_url=*/absl::nullopt,
+          /*set_priority=*/0,
+          /*has_set_priority=*/false,
+          /*pa_requests=*/std::move(pa_requests),
+          /*errors=*/std::vector<std::string>());
       return;
     }
 
-    std::move(generate_bid_callback_)
-        .Run(auction_worklet::mojom::BidderWorkletBid::New(
-                 "ad", *bid, render_url, ad_component_urls, duration),
-             bidding_signals_data_version.value_or(0),
-             bidding_signals_data_version.has_value(), debug_loss_report_url,
-             debug_win_report_url,
-             /*set_priority=*/0,
-             /*has_set_priority=*/false,
-             /*pa_requests=*/std::move(pa_requests),
-             /*errors=*/std::vector<std::string>());
+    generate_bid_client_->OnGenerateBidComplete(
+        auction_worklet::mojom::BidderWorkletBid::New(
+            "ad", *bid, render_url, ad_component_urls, duration),
+        bidding_signals_data_version.value_or(0),
+        bidding_signals_data_version.has_value(), debug_loss_report_url,
+        debug_win_report_url,
+        /*set_priority=*/0,
+        /*has_set_priority=*/false,
+        /*pa_requests=*/std::move(pa_requests),
+        /*errors=*/std::vector<std::string>());
   }
 
   void WaitForReportWin() {
-    DCHECK(!generate_bid_callback_);
+    DCHECK(!generate_bid_client_);
     DCHECK(!report_win_run_loop_);
     if (!report_win_callback_) {
       report_win_run_loop_ = std::make_unique<base::RunLoop>();
@@ -1020,7 +1022,8 @@
  private:
   void OnPipeClosed() { pipe_closed_ = true; }
 
-  BidderWorklet::GenerateBidCallback generate_bid_callback_;
+  mojo::AssociatedRemote<auction_worklet::mojom::GenerateBidClient>
+      generate_bid_client_;
 
   bool pipe_closed_ = false;
 
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index 87bd2ef2..5141b36 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -136,7 +136,8 @@
       auction_worklet::mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
       base::Time auction_start_time,
       uint64_t trace_id,
-      GenerateBidCallback generate_bid_callback) override {
+      mojo::PendingAssociatedRemote<auction_worklet::mojom::GenerateBidClient>
+          generate_bid_client) override {
     NOTREACHED();
   }
 
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index f3f9ca6..ea997eba 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -38,6 +38,9 @@
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -197,7 +200,8 @@
 // * Calling BidderWorklet::GenerateBid().
 // * Tracking how many interest groups the buyer owns that still need to
 // bid.
-class InterestGroupAuction::BuyerHelper {
+class InterestGroupAuction::BuyerHelper
+    : public auction_worklet::mojom::GenerateBidClient {
  public:
   // `auction` is expected to own the BuyerHelper, and therefore outlive it.
   BuyerHelper(InterestGroupAuction* auction,
@@ -248,7 +252,7 @@
     }
   }
 
-  ~BuyerHelper() = default;
+  ~BuyerHelper() override = default;
 
   // Requests bidder worklets and starts generating bids. May generate no bids,
   // 1 bid, or multiple bids. Invokes owning InterestGroupAuction's
@@ -272,15 +276,37 @@
     }
   }
 
+  // auction_worklet::mojom::GenerateBidClient implementation:
+  void OnGenerateBidComplete(
+      auction_worklet::mojom::BidderWorkletBidPtr mojo_bid,
+      uint32_t bidding_signals_data_version,
+      bool has_bidding_signals_data_version,
+      const absl::optional<GURL>& debug_loss_report_url,
+      const absl::optional<GURL>& debug_win_report_url,
+      double set_priority,
+      bool has_set_priority,
+      PrivateAggregationRequests pa_requests,
+      const std::vector<std::string>& errors) override {
+    OnGenerateBidCompleteInternal(
+        generate_bid_client_receiver_set_.current_context(),
+        std::move(mojo_bid), bidding_signals_data_version,
+        has_bidding_signals_data_version, debug_loss_report_url,
+        debug_win_report_url, set_priority, has_set_priority,
+        std::move(pa_requests), errors);
+  }
+
   // Closes all Mojo pipes and release all weak pointers.
   void ClosePipes() {
     // This is needed in addition to closing worklet pipes since the callbacks
     // passed to Mojo pipes this class doesn't own aren't cancellable.
     weak_ptr_factory_.InvalidateWeakPtrs();
 
-    for (BidState& bid_state : bid_states_) {
-      bid_state.worklet_handle.reset();
+    for (auto& bid_state : bid_states_) {
+      CloseBidStatePipes(bid_state);
     }
+    // No need to clear `generate_bid_client_receiver_set_`, since
+    // CloseBidStatePipes() should take care of that.
+    DCHECK(generate_bid_client_receiver_set_.empty());
   }
 
   // Returns true if this buyer has any interest groups that will potentially
@@ -373,7 +399,7 @@
         AuctionWorkletManager::FatalErrorType::kWorkletCrash) {
       // Ignore default error message in case of crash. Instead, use a more
       // specific one.
-      OnGenerateBidComplete(
+      OnGenerateBidCompleteInternal(
           bid_state, auction_worklet::mojom::BidderWorkletBidPtr(),
           /*bidding_signals_data_version=*/0,
           /*has_bidding_signals_data_version=*/false,
@@ -388,14 +414,14 @@
     }
 
     // Otherwise, use error message from the worklet.
-    OnGenerateBidComplete(bid_state,
-                          auction_worklet::mojom::BidderWorkletBidPtr(),
-                          /*bidding_signals_data_version=*/0,
-                          /*has_bidding_signals_data_version=*/false,
-                          /*debug_loss_report_url=*/absl::nullopt,
-                          /*debug_win_report_url=*/absl::nullopt,
-                          /*set_priority=*/0, /*has_set_priority=*/false,
-                          /*pa_requests=*/{}, errors);
+    OnGenerateBidCompleteInternal(
+        bid_state, auction_worklet::mojom::BidderWorkletBidPtr(),
+        /*bidding_signals_data_version=*/0,
+        /*has_bidding_signals_data_version=*/false,
+        /*debug_loss_report_url=*/absl::nullopt,
+        /*debug_win_report_url=*/absl::nullopt,
+        /*set_priority=*/0, /*has_set_priority=*/false,
+        /*pa_requests=*/{}, errors);
   }
 
   // Invoked whenever the AuctionWorkletManager has provided a BidderWorket
@@ -408,6 +434,12 @@
     TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("fledge", "bidder_worklet_generate_bid",
                                       *bid_state->trace_id);
 
+    mojo::PendingAssociatedRemote<auction_worklet::mojom::GenerateBidClient>
+        pending_remote;
+    bid_state->generate_bid_client_receiver_id =
+        generate_bid_client_receiver_set_.Add(
+            this, pending_remote.InitWithNewEndpointAndPassReceiver(),
+            bid_state);
     bid_state->worklet_handle->GetBidderWorklet()->GenerateBid(
         auction_worklet::mojom::BidderWorkletNonSharedParams::New(
             interest_group.name, interest_group.execution_mode,
@@ -423,8 +455,7 @@
                           : absl::optional<url::Origin>(),
         bid_state->bidder.bidding_browser_signals.Clone(),
         auction_->auction_start_time_, *bid_state->trace_id,
-        base::BindOnce(&BuyerHelper::OnGenerateBidComplete,
-                       weak_ptr_factory_.GetWeakPtr(), bid_state));
+        std::move(pending_remote));
 
     // Invoke SendPendingSignalsRequests() asynchronously, if necessary. Do this
     // asynchronously so that all GenerateBid() calls that share a BidderWorklet
@@ -445,7 +476,7 @@
   // Called once a bid has been generated, or has failed to be generated.
   // Releases the BidderWorklet handle and instructs the SellerWorklet to
   // start scoring the bid, if there is one.
-  void OnGenerateBidComplete(
+  void OnGenerateBidCompleteInternal(
       BidState* state,
       auction_worklet::mojom::BidderWorkletBidPtr mojo_bid,
       uint32_t bidding_signals_data_version,
@@ -489,10 +520,6 @@
     auction_->errors_.insert(auction_->errors_.end(), errors.begin(),
                              errors.end());
 
-    // Release the worklet. If it wins the auction, it will be requested again
-    // to invoke its ReportWin() method.
-    state->worklet_handle.reset();
-
     // Ignore invalid bids.
     std::unique_ptr<Bid> bid;
     std::string ad_metadata;
@@ -509,6 +536,10 @@
       state->bidder_debug_loss_report_url = std::move(debug_loss_report_url);
     }
 
+    // Release the worklet. If it wins the auction, it will be requested again
+    // to invoke its ReportWin() method.
+    CloseBidStatePipes(*state);
+
     if (!bid) {
       state->EndTracing();
     } else {
@@ -548,12 +579,13 @@
       const absl::optional<GURL>& debug_loss_report_url,
       const absl::optional<GURL>& debug_win_report_url) {
     if (!IsValidBid(mojo_bid->bid)) {
-      mojo::ReportBadMessage("Invalid bid value");
+      generate_bid_client_receiver_set_.ReportBadMessage("Invalid bid value");
       return nullptr;
     }
 
     if (mojo_bid->bid_duration.is_negative()) {
-      mojo::ReportBadMessage("Invalid bid duration");
+      generate_bid_client_receiver_set_.ReportBadMessage(
+          "Invalid bid duration");
       return nullptr;
     }
 
@@ -562,7 +594,8 @@
     const blink::InterestGroup::Ad* matching_ad =
         FindMatchingAd(*interest_group.ads, mojo_bid->render_url);
     if (!matching_ad) {
-      mojo::ReportBadMessage("Bid render URL must be a valid ad URL");
+      generate_bid_client_receiver_set_.ReportBadMessage(
+          "Bid render URL must be a valid ad URL");
       return nullptr;
     }
 
@@ -572,12 +605,14 @@
       // Only InterestGroups with ad components should return bids with ad
       // components.
       if (!interest_group.ad_components) {
-        mojo::ReportBadMessage("Unexpected non-null ad component list");
+        generate_bid_client_receiver_set_.ReportBadMessage(
+            "Unexpected non-null ad component list");
         return nullptr;
       }
 
       if (mojo_bid->ad_components->size() > blink::kMaxAdAuctionAdComponents) {
-        mojo::ReportBadMessage("Too many ad component URLs");
+        generate_bid_client_receiver_set_.ReportBadMessage(
+            "Too many ad component URLs");
         return nullptr;
       }
 
@@ -585,7 +620,7 @@
       // group's `ad_components` field.
       for (const GURL& ad_component_url : *mojo_bid->ad_components) {
         if (!FindMatchingAd(*interest_group.ad_components, ad_component_url)) {
-          mojo::ReportBadMessage(
+          generate_bid_client_receiver_set_.ReportBadMessage(
               "Bid ad components URL must match a valid ad component URL");
           return nullptr;
         }
@@ -596,12 +631,14 @@
     // Validate `debug_loss_report_url` and `debug_win_report_url`, if present.
     if (debug_loss_report_url.has_value() &&
         !IsUrlValid(debug_loss_report_url.value())) {
-      mojo::ReportBadMessage("Invalid bidder debugging loss report URL");
+      generate_bid_client_receiver_set_.ReportBadMessage(
+          "Invalid bidder debugging loss report URL");
       return nullptr;
     }
     if (debug_win_report_url.has_value() &&
         !IsUrlValid(debug_win_report_url.value())) {
-      mojo::ReportBadMessage("Invalid bidder debugging win report URL");
+      generate_bid_client_receiver_set_.ReportBadMessage(
+          "Invalid bidder debugging win report URL");
       return nullptr;
     }
 
@@ -611,6 +648,16 @@
         bidding_signals_data_version, matching_ad, &bid_state, auction_);
   }
 
+  // Close all Mojo pipes associated with `state`.
+  void CloseBidStatePipes(BidState& state) {
+    state.worklet_handle.reset();
+    if (state.generate_bid_client_receiver_id) {
+      generate_bid_client_receiver_set_.Remove(
+          *state.generate_bid_client_receiver_id);
+      state.generate_bid_client_receiver_id.reset();
+    }
+  }
+
   const raw_ptr<InterestGroupAuction> auction_;
 
   const url::Origin owner_;
@@ -618,6 +665,11 @@
   // State of loaded interest groups owned by `owner_`.
   std::vector<BidState> bid_states_;
 
+  // Per-BidState receivers.
+  mojo::AssociatedReceiverSet<auction_worklet::mojom::GenerateBidClient,
+                              BidState*>
+      generate_bid_client_receiver_set_;
+
   int num_outstanding_bids_ = 0;
 
   base::WeakPtrFactory<BuyerHelper> weak_ptr_factory_{this};
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index eb19471d..50ffdbf 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -26,6 +26,7 @@
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom-forward.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -191,6 +192,10 @@
     // event.
     absl::optional<uint64_t> trace_id;
 
+    // ReceiverId for use as a GenerateBidClient. Only populated while
+    // generateBid() is running.
+    absl::optional<mojo::ReceiverId> generate_bid_client_receiver_id;
+
     // True if the worklet successfully made a bid.
     bool made_bid = false;
 
diff --git a/content/browser/loader/prefetch_url_loader.cc b/content/browser/loader/prefetch_url_loader.cc
index 74b1931..10303c12 100644
--- a/content/browser/loader/prefetch_url_loader.cc
+++ b/content/browser/loader/prefetch_url_loader.cc
@@ -73,8 +73,7 @@
     resource_request_.headers.SetHeader(
         net::HttpRequestHeaders::kAccept,
         kSignedExchangeEnabledAcceptHeaderForPrefetch);
-    if (prefetched_signed_exchange_cache &&
-        resource_request.is_signed_exchange_prefetch_cache_enabled) {
+    if (prefetched_signed_exchange_cache) {
       prefetched_signed_exchange_cache_adapter_ =
           std::make_unique<PrefetchedSignedExchangeCacheAdapter>(
               std::move(prefetched_signed_exchange_cache),
diff --git a/content/browser/loader/prefetch_url_loader.h b/content/browser/loader/prefetch_url_loader.h
index 8e0fe1de..1ba8755 100644
--- a/content/browser/loader/prefetch_url_loader.h
+++ b/content/browser/loader/prefetch_url_loader.h
@@ -149,8 +149,8 @@
   scoped_refptr<SignedExchangePrefetchMetricRecorder>
       signed_exchange_prefetch_metric_recorder_;
 
-  // Used when SignedExchangeSubresourcePrefetch is enabled to store the
-  // prefetched signed exchanges to a PrefetchedSignedExchangeCache.
+  // Used to store the prefetched signed exchanges to a
+  // PrefetchedSignedExchangeCache.
   std::unique_ptr<PrefetchedSignedExchangeCacheAdapter>
       prefetched_signed_exchange_cache_adapter_;
 
diff --git a/content/browser/navigation_subresource_loader_params.h b/content/browser/navigation_subresource_loader_params.h
index c6180b8..141016f 100644
--- a/content/browser/navigation_subresource_loader_params.h
+++ b/content/browser/navigation_subresource_loader_params.h
@@ -38,7 +38,6 @@
   blink::mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info;
   base::WeakPtr<ServiceWorkerObjectHost> controller_service_worker_object_host;
 
-  // For SignedExchangeSubresourcePrefetch.
   // When signed exchanges were prefetched in the previous page and were stored
   // to the PrefetchedSignedExchangeCache, and the main resource for the
   // navigation was served from the cache, |prefetched_signed_exchanges|
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index ba8f759..dfd6978 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -1792,12 +1792,12 @@
   // prior (most likely <a download>) download attempt.
   bool from_download_cross_origin_redirect_ = false;
 
-  // Used when SignedExchangeSubresourcePrefetch is enabled to hold the
-  // prefetched signed exchanges. This is shared with the navigation initiator's
-  // RenderFrameHostImpl. This also means that only the navigations that were
-  // directly initiated by the frame that made the prefetches could use the
-  // prefetched resources, which is a different behavior from regular prefetches
-  // (where all prefetched resources are stored and shared in http cache).
+  // Used to hold prefetched signed exchanges. This is shared with the
+  // navigation initiator's RenderFrameHostImpl. This also means that only the
+  // navigations that were directly initiated by the frame that made the
+  // prefetches could use the prefetched resources, which is a different
+  // behavior from regular prefetches (where all prefetched resources are
+  // stored and shared in http cache).
   scoped_refptr<PrefetchedSignedExchangeCache>
       prefetched_signed_exchange_cache_;
 
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index d311593..8813af6 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1725,12 +1725,17 @@
 void ServiceWorkerVersion::CountFeature(blink::mojom::WebFeature feature) {
   if (!used_features_.insert(feature).second)
     return;
+
+  // TODO(crbug.com/1253581 crbug.com/1021718): Speculative bug fix code.
+  // Take snapshot of the `controllee_map_` instead of iterating on it directly.
+  // This is to rule out the possibility of `controllee_map_` being modified
+  // while we call `CountFeature`.
+  std::vector<base::WeakPtr<ServiceWorkerContainerHost>> hosts_snapshot;
+  hosts_snapshot.reserve(controllee_map_.size());
   for (auto container_host_by_uuid : controllee_map_) {
-    const base::WeakPtr<ServiceWorkerContainerHost>& container_host =
-        container_host_by_uuid.second;
-    // TODO(crbug.com/1253581 crbug.com/1021718): controllee_map_ should be only
-    // containing live container hosts. The below "if" check is a workaround for
-    // unmatched AddControllee / RemoveControllee calls.
+    hosts_snapshot.push_back(container_host_by_uuid.second);
+  }
+  for (auto container_host : hosts_snapshot) {
     if (container_host)
       container_host->CountFeature(feature);
   }
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index 519dc3a..2be2828 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -252,22 +252,10 @@
 };
 
 class SignedExchangeRequestHandlerBrowserTest
-    : public testing::WithParamInterface<
-          std::tuple<bool /* use_prefetch */,
-                     bool /* sxg_subresource_prefetch_enabled */>>,
+    : public testing::WithParamInterface<bool>,
       public SignedExchangeRequestHandlerBrowserTestBase {
  public:
-  SignedExchangeRequestHandlerBrowserTest() {
-    std::tie(use_prefetch_, sxg_subresource_prefetch_enabled_) = GetParam();
-    std::vector<base::Feature> enable_features;
-    std::vector<base::Feature> disabled_features;
-    if (sxg_subresource_prefetch_enabled_) {
-      enable_features.push_back(features::kSignedExchangeSubresourcePrefetch);
-    } else {
-      disabled_features.push_back(features::kSignedExchangeSubresourcePrefetch);
-    }
-    feature_list_.InitWithFeatures(enable_features, disabled_features);
-  }
+  SignedExchangeRequestHandlerBrowserTest() { use_prefetch_ = GetParam(); }
 
   SignedExchangeRequestHandlerBrowserTest(
       const SignedExchangeRequestHandlerBrowserTest&) = delete;
@@ -278,9 +266,6 @@
 
  protected:
   bool UsePrefetch() const { return use_prefetch_; }
-  bool SXGPrefetchCacheIsEnabled() const {
-    return sxg_subresource_prefetch_enabled_;
-  }
 
   void MaybeTriggerPrefetchSXG(const GURL& url, bool expect_success) {
     if (!UsePrefetch())
@@ -293,7 +278,7 @@
     EXPECT_TRUE(NavigateToURL(shell(), prefetch_html_url));
     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 
-    if (SXGPrefetchCacheIsEnabled() && expect_success)
+    if (expect_success)
       WaitUntilSXGIsCached(url);
   }
 
@@ -337,8 +322,6 @@
   }
 
   bool use_prefetch_ = false;
-  bool sxg_subresource_prefetch_enabled_ = false;
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, Simple) {
@@ -395,24 +378,15 @@
   // Wait for the previous page's RFH to be deleted (if it changed) so that the
   // histograms will get updated.
   inactive_rfh_deletion_observer_->Wait();
-  histogram_tester_.ExpectUniqueSample(
-      kLoadResultHistogram, SignedExchangeLoadResult::kSuccess,
-      (UsePrefetch() && !SXGPrefetchCacheIsEnabled()) ? 2 : 1);
+  histogram_tester_.ExpectUniqueSample(kLoadResultHistogram,
+                                       SignedExchangeLoadResult::kSuccess, 1);
   histogram_tester_.ExpectTotalCount(
-      "SignedExchange.Time.CertificateFetch.Success",
-      (UsePrefetch() && !SXGPrefetchCacheIsEnabled()) ? 2 : 1);
+      "SignedExchange.Time.CertificateFetch.Success", 1);
   if (UsePrefetch()) {
     histogram_tester_.ExpectUniqueSample(kPrefetchResultHistogram,
                                          SignedExchangeLoadResult::kSuccess, 1);
-    if (SXGPrefetchCacheIsEnabled()) {
-      histogram_tester_.ExpectTotalCount("PrefetchedSignedExchangeCache.Count",
-                                         1);
-    } else {
-      histogram_tester_.ExpectUniqueSample(
-          "SignedExchange.Prefetch.Recall.30Seconds", true, 1);
-      histogram_tester_.ExpectUniqueSample(
-          "SignedExchange.Prefetch.Precision.30Seconds", true, 1);
-    }
+    histogram_tester_.ExpectTotalCount("PrefetchedSignedExchangeCache.Count",
+                                       1);
   } else {
     histogram_tester_.ExpectUniqueSample(
         "SignedExchange.Prefetch.Recall.30Seconds", false, 1);
@@ -452,10 +426,9 @@
   // Wait for the previous page's RFH to be deleted (if it changed) so that the
   // histograms will get updated.
   inactive_rfh_deletion_observer_->Wait();
-  histogram_tester_.ExpectUniqueSample(
-      kLoadResultHistogram, SignedExchangeLoadResult::kSuccess,
-      (UsePrefetch() && !SXGPrefetchCacheIsEnabled()) ? 2 : 1);
-  if ((UsePrefetch() && SXGPrefetchCacheIsEnabled())) {
+  histogram_tester_.ExpectUniqueSample(kLoadResultHistogram,
+                                       SignedExchangeLoadResult::kSuccess, 1);
+  if (UsePrefetch()) {
     histogram_tester_.ExpectTotalCount("PrefetchedSignedExchangeCache.Count",
                                        1);
   }
@@ -720,9 +693,8 @@
              base::StringPrintf("location.href = '%s';", url.spec().c_str())));
   EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
 
-  histogram_tester_.ExpectUniqueSample(
-      kLoadResultHistogram, SignedExchangeLoadResult::kSuccess,
-      (UsePrefetch() && !SXGPrefetchCacheIsEnabled()) ? 2 : 1);
+  histogram_tester_.ExpectUniqueSample(kLoadResultHistogram,
+                                       SignedExchangeLoadResult::kSuccess, 1);
 }
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
@@ -779,8 +751,7 @@
 
 INSTANTIATE_TEST_SUITE_P(All,
                          SignedExchangeRequestHandlerBrowserTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Bool()));
+                         ::testing::Bool());
 
 class SignedExchangeRequestHandlerDownloadBrowserTest
     : public SignedExchangeRequestHandlerBrowserTestBase {
@@ -1686,8 +1657,7 @@
 
 INSTANTIATE_TEST_SUITE_P(All,
                          SignedExchangeExpectCTReportBrowserTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Bool()));
+                         ::testing::Bool());
 
 #if BUILDFLAG(ENABLE_REPORTING)
 
@@ -1835,8 +1805,7 @@
 
 INSTANTIATE_TEST_SUITE_P(All,
                          SignedExchangeReportingBrowserTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Bool()));
+                         ::testing::Bool());
 
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
@@ -1946,9 +1915,6 @@
       UsePrefetch() ? 2 : 1);
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         SignedExchangePKPBrowserTest,
-                         ::testing::Combine(::testing::Bool(),
-                                            ::testing::Bool()));
+INSTANTIATE_TEST_SUITE_P(All, SignedExchangePKPBrowserTest, ::testing::Bool());
 
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
index 9cb9227..6ce4d5a0 100644
--- a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
@@ -197,10 +197,7 @@
 
 }  // namespace
 
-class SignedExchangePrefetchBrowserTest
-    : public testing::WithParamInterface<
-          bool /* sxg_subresource_prefetch_enabled */>,
-      public PrefetchBrowserTestBase {
+class SignedExchangePrefetchBrowserTest : public PrefetchBrowserTestBase {
  public:
   SignedExchangePrefetchBrowserTest() {
     // Call MockClock::Get() here to initialize ScopedMockClockOverride which
@@ -213,22 +210,15 @@
   SignedExchangePrefetchBrowserTest& operator=(
       const SignedExchangePrefetchBrowserTest&) = delete;
 
-  ~SignedExchangePrefetchBrowserTest() = default;
+  ~SignedExchangePrefetchBrowserTest() override = default;
 
   void SetUp() override {
-    bool sxg_subresource_prefetch_enabled = GetParam();
-
     std::vector<base::Feature> enable_features;
     std::vector<base::Feature> disabled_features;
     enable_features.push_back(features::kSignedHTTPExchange);
     // Need to run the network service in process for testing cache expirity
     // (PrefetchMainResourceSXG_ExceedPrefetchReuseMins) using MockClock.
     enable_features.push_back(features::kNetworkServiceInProcess);
-    if (sxg_subresource_prefetch_enabled) {
-      enable_features.push_back(features::kSignedExchangeSubresourcePrefetch);
-    } else {
-      disabled_features.push_back(features::kSignedExchangeSubresourcePrefetch);
-    }
     feature_list_.InitWithFeatures(enable_features, disabled_features);
     PrefetchBrowserTestBase::SetUp();
   }
@@ -252,8 +242,6 @@
   std::unique_ptr<InactiveRenderFrameHostDeletionObserver>
       inactive_rfh_deletion_observer_;
 
-  bool IsSignedExchangeSubresourcePrefetchEnabled() { return GetParam(); }
-
   void SetBlobLimits() {
     scoped_refptr<ChromeBlobStorageContext> blob_context =
         ChromeBlobStorageContext::GetFor(
@@ -287,50 +275,6 @@
     const GURL inner_url =
         embedded_test_server()->GetURL(inner_url_hostname, inner_url_path);
 
-    if (!IsSignedExchangeSubresourcePrefetchEnabled()) {
-      EXPECT_TRUE(GetCachedExchanges(shell()).empty());
-
-      // Shutdown the server.
-      EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
-
-      // The HTTP Cache does not currently support cross-origin prefetching if
-      // the split cache is enabled, so skip the rest of the tests.
-      // TODO(crbug.com/939317): Remove this early return when cross-origin
-      // prefetching with split cache works.
-      if (base::FeatureList::IsEnabled(
-              net::features::kSplitCacheByNetworkIsolationKey) &&
-          !url::Origin::Create(sxg_url).IsSameOriginWith(
-              url::Origin::Create(inner_url))) {
-        return;
-      }
-
-      // Need to setup MockSignedExchangeHandlerFactory because the SXG is
-      // loaded from HTTPCache.
-      MockSignedExchangeHandlerFactory factory({MockSignedExchangeHandlerParams(
-          sxg_url, SignedExchangeLoadResult::kSuccess, net::OK, inner_url,
-          "text/html", {}, header_integrity,
-          base::Time() /* signature_expire_time */)});
-      ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
-
-      base::HistogramTester histograms;
-      // Subsequent navigation to the target URL wouldn't hit the network for
-      // the target URL. The target content should still be read correctly.
-      // The content is loaded from HTTPCache.
-      NavigateToURLAndWaitTitle(sxg_url, "Prefetch Target (SXG)");
-      // Wait for the previous page's RFH to be deleted (if it changed) so that
-      // the histograms will get updated.
-      inactive_rfh_deletion_observer_->Wait();
-
-      EXPECT_EQ(1, sxg_request_counter->GetRequestCount());
-      histograms.ExpectTotalCount("PrefetchedSignedExchangeCache.Count", 0);
-      histograms.ExpectTotalCount("PrefetchedSignedExchangeCache.BodySize", 0);
-      histograms.ExpectTotalCount("PrefetchedSignedExchangeCache.BodySizeTotal",
-                                  0);
-      histograms.ExpectTotalCount(
-          "PrefetchedSignedExchangeCache.HeadersSizeTotal", 0);
-      return;
-    }
-
     const auto cached_exchanges = GetCachedExchanges(shell());
     EXPECT_EQ(1u, cached_exchanges.size());
     const auto it = cached_exchanges.find(sxg_url);
@@ -430,7 +374,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_SameOrigin) {
   RunPrefetchMainResourceSXGTest("example.com" /* prefetch_page_hostname */,
                                  "/prefetch.html" /* prefetch_page_path */,
@@ -440,7 +384,7 @@
                                  "/target.html" /* inner_url_path */);
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_CrossOrigin) {
   RunPrefetchMainResourceSXGTest(
       "aggregator.example.com" /* prefetch_page_hostname */,
@@ -451,7 +395,7 @@
       "/target.html" /* inner_url_path */);
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_SameURL) {
   RunPrefetchMainResourceSXGTest("example.com" /* prefetch_page_hostname */,
                                  "/prefetch.html" /* prefetch_page_path */,
@@ -461,7 +405,7 @@
                                  "/target.html" /* inner_url_path */);
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_BlobStorageLimit) {
   SetBlobLimits();
 
@@ -483,7 +427,7 @@
   EXPECT_EQ(0u, cached_exchanges.size());
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     SignedExchangePrefetchBrowserTest,
     PrefetchMainResourceSXG_BlobStorageLimitWithContentLength) {
   // BlobBuilderFromStream's behavior is different when "content-length"
@@ -510,7 +454,7 @@
   EXPECT_EQ(0u, cached_exchanges.size());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_CacheControlNoStore) {
   const std::string content =
       "<head><title>Prefetch Target (SXG)</title></head>";
@@ -530,7 +474,7 @@
   EXPECT_EQ(0u, cached_exchanges.size());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_VaryAsteriskHeader) {
   const std::string content =
       "<head><title>Prefetch Target (SXG)</title></head>";
@@ -549,7 +493,7 @@
   EXPECT_EQ(0u, cached_exchanges.size());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_VaryAcceptEncodingHeader) {
   const std::string content =
       "<head><title>Prefetch Target (SXG)</title></head>";
@@ -566,11 +510,10 @@
   // The signed exchange which response header has "vary: accept-encoding"
   // header should be stored to the cache.
   const auto cached_exchanges = GetCachedExchanges(shell());
-  EXPECT_EQ(IsSignedExchangeSubresourcePrefetchEnabled() ? 1u : 0u,
-            cached_exchanges.size());
+  EXPECT_EQ(1u, cached_exchanges.size());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_ExceedPrefetchReuseMins) {
   const char* hostname = "example.com";
   const char* sxg_path = "/target.sxg";
@@ -591,8 +534,7 @@
   const GURL inner_url =
       embedded_test_server()->GetURL(hostname, inner_url_path);
 
-  EXPECT_EQ(IsSignedExchangeSubresourcePrefetchEnabled() ? 1u : 0u,
-            GetCachedExchanges(shell()).size());
+  EXPECT_EQ(1u, GetCachedExchanges(shell()).size());
 
   MockClock::Get().Advance(
       base::Seconds(net::HttpCache::kPrefetchReuseMins * 60 + 1));
@@ -611,7 +553,7 @@
   EXPECT_EQ(2, sxg_request_counter->GetRequestCount());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_CacheControlPublic) {
   const char* hostname = "example.com";
   const char* sxg_path = "/target.sxg";
@@ -636,30 +578,18 @@
   const GURL inner_url =
       embedded_test_server()->GetURL(hostname, inner_url_path);
 
-  EXPECT_EQ(IsSignedExchangeSubresourcePrefetchEnabled() ? 1u : 0u,
-            GetCachedExchanges(shell()).size());
+  EXPECT_EQ(1u, GetCachedExchanges(shell()).size());
 
   MockClock::Get().Advance(
       base::Seconds(net::HttpCache::kPrefetchReuseMins * 2 * 60));
 
-  if (IsSignedExchangeSubresourcePrefetchEnabled()) {
-    NavigateToURLAndWaitTitle(sxg_url, "Prefetch Target (SXG)");
-  } else {
-    // Need to setup MockSignedExchangeHandlerFactory because the SXG is loaded
-    // from HTTPCache.
-    MockSignedExchangeHandlerFactory factory({MockSignedExchangeHandlerParams(
-        sxg_url, SignedExchangeLoadResult::kSuccess, net::OK, inner_url,
-        "text/html", {}, header_integrity,
-        base::Time() /* signature_expire_time */)});
-    ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
-    NavigateToURLAndWaitTitle(sxg_url, "Prefetch Target (SXG)");
-  }
+  NavigateToURLAndWaitTitle(sxg_url, "Prefetch Target (SXG)");
 
   // SXG must Not be fetched again.
   EXPECT_EQ(1, sxg_request_counter->GetRequestCount());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        PrefetchMainResourceSXG_CacheControlPublicExpire) {
   const char* hostname = "example.com";
   const char* sxg_path = "/target.sxg";
@@ -684,8 +614,7 @@
   const GURL inner_url =
       embedded_test_server()->GetURL(hostname, inner_url_path);
 
-  EXPECT_EQ(IsSignedExchangeSubresourcePrefetchEnabled() ? 1u : 0u,
-            GetCachedExchanges(shell()).size());
+  EXPECT_EQ(1u, GetCachedExchanges(shell()).size());
 
   MockClock::Get().Advance(
       base::Seconds(net::HttpCache::kPrefetchReuseMins * 3 * 60 + 1));
@@ -712,7 +641,7 @@
 #define MAYBE_PrefetchMainResourceSXG_SignatureExpire \
   PrefetchMainResourceSXG_SignatureExpire
 #endif
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest,
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest,
                        MAYBE_PrefetchMainResourceSXG_SignatureExpire) {
   const char* hostname = "example.com";
   const char* sxg_path = "/target.sxg";
@@ -735,8 +664,7 @@
   const GURL inner_url =
       embedded_test_server()->GetURL(hostname, inner_url_path);
 
-  EXPECT_EQ(IsSignedExchangeSubresourcePrefetchEnabled() ? 1u : 0u,
-            GetCachedExchanges(shell()).size());
+  EXPECT_EQ(1u, GetCachedExchanges(shell()).size());
 
   MockClock::Get().Advance(
       base::Minutes(net::HttpCache::kPrefetchReuseMins * 3));
@@ -763,7 +691,7 @@
   EXPECT_EQ(2, sxg_request_counter->GetRequestCount());
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangePrefetchBrowserTest, ClearAll) {
+IN_PROC_BROWSER_TEST_F(SignedExchangePrefetchBrowserTest, ClearAll) {
   const char* hostname = "example.com";
   const char* sxg_path = "/target.sxg";
   const char* inner_url_path = "/target.html";
@@ -783,8 +711,7 @@
   const GURL inner_url =
       embedded_test_server()->GetURL(hostname, inner_url_path);
 
-  EXPECT_EQ(IsSignedExchangeSubresourcePrefetchEnabled() ? 1u : 0u,
-            GetCachedExchanges(shell()).size());
+  EXPECT_EQ(1u, GetCachedExchanges(shell()).size());
 
   BrowsingDataRemover* remover =
       shell()->web_contents()->GetBrowserContext()->GetBrowsingDataRemover();
@@ -809,10 +736,6 @@
   EXPECT_EQ(2, sxg_request_counter->GetRequestCount());
 }
 
-INSTANTIATE_TEST_SUITE_P(SignedExchangePrefetchBrowserTest,
-                         SignedExchangePrefetchBrowserTest,
-                         ::testing::Bool());
-
 class SignedExchangeSubresourcePrefetchBrowserTest
     : public PrefetchBrowserTestBase {
  public:
@@ -840,7 +763,6 @@
     std::vector<base::Feature> enable_features;
     std::vector<base::Feature> disabled_features;
     enable_features.push_back(features::kSignedHTTPExchange);
-    enable_features.push_back(features::kSignedExchangeSubresourcePrefetch);
     // Need to run the network service in process for testing cache expirity
     // (PrefetchMainResourceSXG_ExceedPrefetchReuseMins) using MockClock.
     enable_features.push_back(features::kNetworkServiceInProcess);
@@ -1211,9 +1133,7 @@
   // SplitCacheByNetworkIsolationKey feature. Note that even if
   // SplitCacheByNetworkIsolationKey feature is enabled, current Chromium
   // implementation doesn't require as=document for prefetching main resource
-  // signed exchanges when SignedExchangePrefetchCacheForNavigations feature is
-  // enabled, and for prefetching main resource and subresource signed exchanges
-  // when SignedExchangeSubresourcePrefetch is enabled.
+  // and subresource signed exchanges.
   RunPrefetchMainResourceSXGAndScriptSXGTest(
       "aggregator.example.com" /* prefetch_page_hostname */,
       "/prefetch.html" /* prefetch_page_path */,
diff --git a/content/browser/webrtc/resources/stats_graph_helper.js b/content/browser/webrtc/resources/stats_graph_helper.js
index d7a69409..e71a60d5 100644
--- a/content/browser/webrtc/resources/stats_graph_helper.js
+++ b/content/browser/webrtc/resources/stats_graph_helper.js
@@ -246,6 +246,15 @@
     if (!graphViews[graphViewId]) {
       graphViews[graphViewId] =
           createStatsGraphView(peerConnectionElement, report, graphType);
+      const searchParameters = new URLSearchParams(window.location.search);
+      if (searchParameters.has('statsInterval')) {
+        const statsInterval = Math.max(
+            parseInt(searchParameters.get('statsInterval'), 10),
+            100);
+        if (isFinite(statsInterval)) {
+          graphViews[graphViewId].setScale(statsInterval);
+        }
+      }
       const date = new Date(stats.timestamp);
       graphViews[graphViewId].setDateRange(date, date);
     }
diff --git a/content/browser/webrtc/resources/webrtc_internals.js b/content/browser/webrtc/resources/webrtc_internals.js
index 6799e2a..a7e5f80 100644
--- a/content/browser/webrtc/resources/webrtc_internals.js
+++ b/content/browser/webrtc/resources/webrtc_internals.js
@@ -144,8 +144,19 @@
         params.eventLogRecordingsToggleable);
   });
 
-  // Requests stats from all peer connections every second.
-  window.setInterval(requestStats, 1000);
+  // Requests stats from all peer connections every second unless specified via
+  // ?statsInterval=(milliseconds >= 100ms)
+  const searchParameters = new URLSearchParams(window.location.search);
+  let statsInterval = 1000;
+  if (searchParameters.has('statsInterval')) {
+    statsInterval = Math.max(
+        parseInt(searchParameters.get('statsInterval'), 10),
+        100);
+    if (!isFinite(statsInterval)) {
+      statsInterval = 1000;
+    }
+  }
+  window.setInterval(requestStats, statsInterval);
 }
 document.addEventListener('DOMContentLoaded', initialize);
 
@@ -336,17 +347,6 @@
         'when attempting to parse the a=extmap-allow-mixed line in the ' +
         'SDP remove the line from the SDP during signalling.');
     }
-    if (data.rtcConfiguration.indexOf('sdpSemantics: "plan-b"') !== -1) {
-      appendChildWithText(deprecationNotices, 'li',
-        'Plan B SDP semantics, which is used when constructing an ' +
-        'RTCPeerConnection with {sdpSemantics:\"plan-b\"}, is a legacy ' +
-        'version of the Session Description Protocol that has severe ' +
-        'compatibility issues on modern browsers. The standardized SDP ' +
-        'format, \"unified-plan\", has been used by default since M72 ' +
-        '(January, 2019). Dropping support for Plan B is targeted for ' +
-        'M93. See https://www.chromestatus.com/feature/5823036655665152 ' +
-        'for more details.');
-    }
   }
   if (data.constraints) {
     if (data.constraints.indexOf('enableDtlsSrtp:') !== -1) {
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 3fb8a747..9416bce 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -275,8 +275,6 @@
     {wf::EnableSharedArrayBufferOnDesktop,
      features::kSharedArrayBufferOnDesktop},
     {wf::EnableSharedAutofill, autofill::features::kAutofillSharedAutofill},
-    {wf::EnableSignedExchangeSubresourcePrefetch,
-     features::kSignedExchangeSubresourcePrefetch},
     {wf::EnableSkipTouchEventFilter, blink::features::kSkipTouchEventFilter},
     {wf::EnableSpeculationRulesPrefetchProxy,
      blink::features::kSpeculationRulesPrefetchProxy, kSetOnlyIfOverridden},
diff --git a/content/public/browser/file_system_access_permission_context.h b/content/public/browser/file_system_access_permission_context.h
index 42019ab9..aa09a19 100644
--- a/content/public/browser/file_system_access_permission_context.h
+++ b/content/public/browser/file_system_access_permission_context.h
@@ -144,9 +144,12 @@
 
   // Return the path associated with well-known directories such as "desktop"
   // and "music", or a default path if the |directory| cannot be matched to a
-  // well-known directory.
+  // well-known directory. When |directory| is WellKnownDirectory.DIR_DOWNLOADS,
+  // |origin| is used to determine if browser-specified download directory
+  // should be returned instead of OS default download directory.
   virtual base::FilePath GetWellKnownDirectoryPath(
-      blink::mojom::WellKnownDirectory directory) = 0;
+      blink::mojom::WellKnownDirectory directory,
+      const url::Origin& origin) = 0;
 
   // Return the desired title of the file picker for the given `options`.
   virtual std::u16string GetPickerTitle(
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 1d8a6de..2f1ec10 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -885,11 +885,6 @@
 const base::Feature kSignedExchangeReportingForDistributors{
     "SignedExchangeReportingForDistributors", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Subresource prefetching+loading via Signed HTTP Exchange
-// https://www.chromestatus.com/feature/5126805474246656
-const base::Feature kSignedExchangeSubresourcePrefetch{
-    "SignedExchangeSubresourcePrefetch", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Origin-Signed HTTP Exchanges (for WebPackage Loading)
 // https://www.chromestatus.com/feature/5745285984681984
 const base::Feature kSignedHTTPExchange{"SignedHTTPExchange",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 95586c2..9a7319e 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -220,7 +220,6 @@
 CONTENT_EXPORT extern const base::Feature kSharedArrayBufferOnDesktop;
 CONTENT_EXPORT extern const base::Feature
     kSignedExchangeReportingForDistributors;
-CONTENT_EXPORT extern const base::Feature kSignedExchangeSubresourcePrefetch;
 CONTENT_EXPORT extern const base::Feature kSignedHTTPExchange;
 CONTENT_EXPORT extern const base::Feature kSignedHTTPExchangePingValidity;
 CONTENT_EXPORT extern const base::Feature
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index d70f6df6..0531a26 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -6108,7 +6108,7 @@
 gfx::RectF RenderFrameImpl::ElementBoundsInWindow(
     const blink::WebElement& element) {
   return gfx::RectF(GetLocalRootWebFrameWidget()->BlinkSpaceToEnclosedDIPs(
-      element.BoundsInViewport()));
+      element.BoundsInWidget()));
 }
 
 void RenderFrameImpl::ConvertViewportToWindow(gfx::Rect* rect) {
diff --git a/content/renderer/render_widget_browsertest.cc b/content/renderer/render_widget_browsertest.cc
index 13ea14e..e608fac 100644
--- a/content/renderer/render_widget_browsertest.cc
+++ b/content/renderer/render_widget_browsertest.cc
@@ -47,7 +47,7 @@
 
   gfx::PointF GetCenterPointOfElement(const blink::WebString& id) {
     auto rect =
-        GetMainFrame()->GetDocument().GetElementById(id).BoundsInViewport();
+        GetMainFrame()->GetDocument().GetElementById(id).BoundsInWidget();
     return gfx::PointF(rect.x() + rect.width() / 2,
                        rect.y() + rect.height() / 2);
   }
diff --git a/content/services/auction_worklet/auction_v8_helper_unittest.cc b/content/services/auction_worklet/auction_v8_helper_unittest.cc
index e9f962f..b7495e9c 100644
--- a/content/services/auction_worklet/auction_v8_helper_unittest.cc
+++ b/content/services/auction_worklet/auction_v8_helper_unittest.cc
@@ -78,7 +78,8 @@
       auction_worklet::mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
       base::Time auction_start_time,
       uint64_t trace_id,
-      GenerateBidCallback generate_bid_callback) override {
+      mojo::PendingAssociatedRemote<mojom::GenerateBidClient>
+          generate_bid_client) override {
     ADD_FAILURE() << "GenerateBid shouldn't be called on DebugConnector";
   }
 
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index f9309eea..b867928 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -36,6 +36,8 @@
 #include "content/services/auction_worklet/worklet_loader.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/struct_ptr.h"
@@ -192,7 +194,8 @@
     mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
     base::Time auction_start_time,
     uint64_t trace_id,
-    GenerateBidCallback generate_bid_callback) {
+    mojo::PendingAssociatedRemote<mojom::GenerateBidClient>
+        generate_bid_client) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
   generate_bid_tasks_.emplace_front();
@@ -211,7 +214,7 @@
       std::move(bidding_browser_signals);
   generate_bid_task->auction_start_time = auction_start_time;
   generate_bid_task->trace_id = trace_id;
-  generate_bid_task->callback = std::move(generate_bid_callback);
+  generate_bid_task->generate_bid_client.Bind(std::move(generate_bid_client));
 
   const auto& trusted_bidding_signals_keys =
       generate_bid_task->bidder_worklet_non_shared_params
@@ -961,11 +964,11 @@
     error_msgs.emplace_back(
         std::move(task->trusted_bidding_signals_error_msg).value());
   }
-  std::move(task->callback)
-      .Run(std::move(bid), bidding_signals_data_version.value_or(0),
-           bidding_signals_data_version.has_value(), debug_loss_report_url,
-           debug_win_report_url, set_priority.value_or(0),
-           set_priority.has_value(), std::move(pa_requests), error_msgs);
+  task->generate_bid_client->OnGenerateBidComplete(
+      std::move(bid), bidding_signals_data_version.value_or(0),
+      bidding_signals_data_version.has_value(), debug_loss_report_url,
+      debug_win_report_url, set_priority.value_or(0), set_priority.has_value(),
+      std::move(pa_requests), error_msgs);
   generate_bid_tasks_.erase(task);
 }
 
diff --git a/content/services/auction_worklet/bidder_worklet.h b/content/services/auction_worklet/bidder_worklet.h
index 3a59a9106..c60caf7 100644
--- a/content/services/auction_worklet/bidder_worklet.h
+++ b/content/services/auction_worklet/bidder_worklet.h
@@ -26,6 +26,7 @@
 #include "content/services/auction_worklet/trusted_signals.h"
 #include "content/services/auction_worklet/trusted_signals_request_manager.h"
 #include "content/services/auction_worklet/worklet_loader.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -113,7 +114,8 @@
       mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
       base::Time auction_start_time,
       uint64_t trace_id,
-      GenerateBidCallback generate_bid_callback) override;
+      mojo::PendingAssociatedRemote<mojom::GenerateBidClient>
+          generate_bid_client) override;
   void SendPendingSignalsRequests() override;
   void ReportWin(
       const std::string& interest_group_name,
@@ -160,7 +162,7 @@
     // reported on bid completion.
     absl::optional<std::string> trusted_bidding_signals_error_msg;
 
-    GenerateBidCallback callback;
+    mojo::AssociatedRemote<mojom::GenerateBidClient> generate_bid_client;
   };
 
   using GenerateBidTaskList = std::list<GenerateBidTask>;
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index b13d16a..5dd88fc 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -28,7 +28,9 @@
 #include "content/services/auction_worklet/worklet_devtools_debug_test_util.h"
 #include "content/services/auction_worklet/worklet_test_util.h"
 #include "content/services/auction_worklet/worklet_v8_debug_test_util.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
 #include "mojo/public/cpp/bindings/struct_ptr.h"
 #include "mojo/public/cpp/bindings/unique_receiver_set.h"
 #include "net/http/http_status_code.h"
@@ -113,6 +115,76 @@
          base::StringPrintf(kReportWinScript, function_body.c_str());
 }
 
+// A GenerateBidClient that takes a callback to call in OnGenerateBid().
+class GenerateBidClientWithCallbacks : public mojom::GenerateBidClient {
+ public:
+  using GenerateBidCallback =
+      base::OnceCallback<void(mojom::BidderWorkletBidPtr bid,
+                              uint32_t data_version,
+                              bool has_data_version,
+                              const absl::optional<GURL>& debug_loss_report_url,
+                              const absl::optional<GURL>& debug_win_report_url,
+                              double set_priority,
+                              bool has_set_priority,
+                              PrivateAggregationRequests pa_requests,
+                              const std::vector<std::string>& errors)>;
+
+  explicit GenerateBidClientWithCallbacks(
+      GenerateBidCallback generate_bid_callback)
+      : generate_bid_callback_(std::move(generate_bid_callback)) {}
+
+  ~GenerateBidClientWithCallbacks() override = default;
+
+  // Helper that creates a GenerateBidClientWithCallbacks whose lifetime is
+  // managed by a self-owned receiver.
+  static mojo::PendingAssociatedRemote<mojom::GenerateBidClient> Create(
+      GenerateBidCallback callback) {
+    mojo::PendingAssociatedRemote<mojom::GenerateBidClient> client_remote;
+    mojo::MakeSelfOwnedAssociatedReceiver(
+        std::make_unique<GenerateBidClientWithCallbacks>(std::move(callback)),
+        client_remote.InitWithNewEndpointAndPassReceiver());
+    return client_remote;
+  }
+
+  // Creates a GenerateBidClient() that expects OnGenerateBidComplete() never to
+  // be invoked. Allows OnBiddingSignalsReceived() to be invoked.
+  static mojo::PendingAssociatedRemote<mojom::GenerateBidClient>
+  CreateNeverCompletes() {
+    return Create(GenerateBidNeverInvokedCallback());
+  }
+
+  static GenerateBidCallback GenerateBidNeverInvokedCallback() {
+    return base::BindOnce([](mojom::BidderWorkletBidPtr bid,
+                             uint32_t data_version, bool has_data_version,
+                             const absl::optional<GURL>& debug_loss_report_url,
+                             const absl::optional<GURL>& debug_win_report_url,
+                             double set_priority, bool has_set_priority,
+                             PrivateAggregationRequests pa_requests,
+                             const std::vector<std::string>& errors) {
+      ADD_FAILURE() << "OnGenerateBidComplete should not be invoked.";
+    });
+  }
+
+  // mojom::GenerateBidClient implementation:
+  void OnGenerateBidComplete(mojom::BidderWorkletBidPtr bid,
+                             uint32_t data_version,
+                             bool has_data_version,
+                             const absl::optional<GURL>& debug_loss_report_url,
+                             const absl::optional<GURL>& debug_win_report_url,
+                             double set_priority,
+                             bool has_set_priority,
+                             PrivateAggregationRequests pa_requests,
+                             const std::vector<std::string>& errors) override {
+    std::move(generate_bid_callback_)
+        .Run(std::move(bid), data_version, has_data_version,
+             debug_loss_report_url, debug_win_report_url, set_priority,
+             has_set_priority, std::move(pa_requests), errors);
+  }
+
+ private:
+  GenerateBidCallback generate_bid_callback_;
+};
+
 class BidderWorkletTest : public testing::Test {
  public:
   BidderWorkletTest() { SetDefaultParameters(); }
@@ -399,36 +471,35 @@
     return bidder_worklet;
   }
 
-  void GenerateBid(mojom::BidderWorklet* bidder_worklet) {
+  // If no `generate_bid_client` is provided, uses one that invokes
+  // GenerateBidCallback().
+  void GenerateBid(mojom::BidderWorklet* bidder_worklet,
+                   mojo::PendingAssociatedRemote<mojom::GenerateBidClient>
+                       generate_bid_client = mojo::NullAssociatedRemote()) {
+    if (!generate_bid_client) {
+      generate_bid_client =
+          GenerateBidClientWithCallbacks::Create(base::BindOnce(
+              &BidderWorkletTest::GenerateBidCallback, base::Unretained(this)));
+    }
     bidder_worklet->GenerateBid(
         CreateBidderWorkletNonSharedParams(), join_origin_, auction_signals_,
         per_buyer_signals_, per_buyer_timeout_, browser_signal_seller_origin_,
         browser_signal_top_level_seller_origin_, CreateBiddingBrowserSignals(),
         auction_start_time_,
-        /*trace_id=*/1,
-        base::BindOnce(&BidderWorkletTest::GenerateBidCallback,
-                       base::Unretained(this)));
+        /*trace_id=*/1, std::move(generate_bid_client));
     bidder_worklet->SendPendingSignalsRequests();
   }
 
-  // Calls GenerateBid(), expecting the callback never to be invoked.
-  void GenerateBidExpectingCallbackNotInvoked(
+  // Calls GenerateBid(), expecting the GenerateBidClient's
+  // OnGenerateBidComplete() method never to be invoked.
+  void GenerateBidExpectingNeverCompletes(
       mojom::BidderWorklet* bidder_worklet) {
     bidder_worklet->GenerateBid(
         CreateBidderWorkletNonSharedParams(), join_origin_, auction_signals_,
         per_buyer_signals_, per_buyer_timeout_, browser_signal_seller_origin_,
         browser_signal_top_level_seller_origin_, CreateBiddingBrowserSignals(),
         auction_start_time_,
-        /*trace_id=*/1,
-        base::BindOnce([](mojom::BidderWorkletBidPtr bid, uint32_t data_version,
-                          bool has_data_version,
-                          const absl::optional<GURL>& debug_loss_report_url,
-                          const absl::optional<GURL>& debug_win_report_url,
-                          double set_priority, bool has_set_priority,
-                          PrivateAggregationRequests pa_requests,
-                          const std::vector<std::string>& errors) {
-          ADD_FAILURE() << "Callback should not be invoked.";
-        }));
+        /*trace_id=*/1, GenerateBidClientWithCallbacks::CreateNeverCompletes());
     bidder_worklet->SendPendingSignalsRequests();
   }
 
@@ -587,7 +658,7 @@
 // invoking it.
 TEST_F(BidderWorkletTest, PipeClosed) {
   auto bidder_worklet = CreateWorklet();
-  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  GenerateBidExpectingNeverCompletes(bidder_worklet.get());
   bidder_worklet.reset();
   EXPECT_FALSE(bidder_worklets_.empty());
 
@@ -601,7 +672,7 @@
                                   CreateBasicGenerateBidScript(),
                                   net::HTTP_NOT_FOUND);
   auto bidder_worklet = CreateWorklet();
-  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  GenerateBidExpectingNeverCompletes(bidder_worklet.get());
   EXPECT_EQ("Failed to load https://url.test/ HTTP status = 404 Not Found.",
             WaitForDisconnect());
 }
@@ -610,7 +681,7 @@
   AddJavascriptResponse(&url_loader_factory_, interest_group_bidding_url_,
                         "Invalid Javascript");
   auto bidder_worklet = CreateWorklet();
-  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  GenerateBidExpectingNeverCompletes(bidder_worklet.get());
 
   std::string error = WaitForDisconnect();
   EXPECT_THAT(error, StartsWith("https://url.test/:1 "));
@@ -1586,7 +1657,7 @@
           browser_signal_top_level_seller_origin_,
           CreateBiddingBrowserSignals(), auction_start_time_,
           /*trace_id=*/1,
-          base::BindLambdaForTesting(
+          GenerateBidClientWithCallbacks::Create(base::BindLambdaForTesting(
               [&run_loop, &num_generate_bid_calls, bid_value](
                   mojom::BidderWorkletBidPtr bid, uint32_t data_version,
                   bool has_data_version,
@@ -1603,7 +1674,7 @@
                 ++num_generate_bid_calls;
                 if (num_generate_bid_calls == kNumGenerateBidCalls)
                   run_loop.Quit();
-              }));
+              })));
     }
 
     // If this is the first loop iteration, wait for all the Mojo calls to
@@ -1630,7 +1701,7 @@
   auto bidder_worklet = CreateWorklet();
 
   for (size_t i = 0; i < 10; ++i) {
-    GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+    GenerateBidExpectingNeverCompletes(bidder_worklet.get());
   }
 
   // Script fails to load.
@@ -1680,7 +1751,7 @@
         browser_signal_top_level_seller_origin_, CreateBiddingBrowserSignals(),
         auction_start_time_,
         /*trace_id=*/1,
-        base::BindLambdaForTesting(
+        GenerateBidClientWithCallbacks::Create(base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, i](
                 mojom::BidderWorkletBidPtr bid, uint32_t data_version,
                 bool has_data_version,
@@ -1698,7 +1769,7 @@
               ++num_generate_bid_calls;
               if (num_generate_bid_calls == kNumGenerateBidCalls)
                 run_loop.Quit();
-            }));
+            })));
   }
   // This should trigger a single network request for all needed signals.
   bidder_worklet->SendPendingSignalsRequests();
@@ -1783,7 +1854,7 @@
         browser_signal_top_level_seller_origin_, CreateBiddingBrowserSignals(),
         auction_start_time_,
         /*trace_id=*/1,
-        base::BindLambdaForTesting(
+        GenerateBidClientWithCallbacks::Create(base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, i](
                 mojom::BidderWorkletBidPtr bid, uint32_t data_version,
                 bool has_data_version,
@@ -1801,7 +1872,7 @@
               ++num_generate_bid_calls;
               if (num_generate_bid_calls == kNumGenerateBidCalls)
                 run_loop.Quit();
-            }));
+            })));
   }
   // This should trigger a single network request for all needed signals.
   bidder_worklet->SendPendingSignalsRequests();
@@ -1892,7 +1963,7 @@
         browser_signal_top_level_seller_origin_, CreateBiddingBrowserSignals(),
         auction_start_time_,
         /*trace_id=*/1,
-        base::BindLambdaForTesting(
+        GenerateBidClientWithCallbacks::Create(base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, i](
                 mojom::BidderWorkletBidPtr bid, uint32_t data_version,
                 bool has_data_version,
@@ -1910,7 +1981,7 @@
               ++num_generate_bid_calls;
               if (num_generate_bid_calls == kNumGenerateBidCalls)
                 run_loop.Quit();
-            }));
+            })));
   }
   // This should trigger a single network request for all needed signals.
   bidder_worklet->SendPendingSignalsRequests();
@@ -1980,7 +2051,7 @@
         browser_signal_top_level_seller_origin_, CreateBiddingBrowserSignals(),
         auction_start_time_,
         /*trace_id=*/1,
-        base::BindLambdaForTesting(
+        GenerateBidClientWithCallbacks::Create(base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, i](
                 mojom::BidderWorkletBidPtr bid, uint32_t data_version,
                 bool has_data_version,
@@ -1998,7 +2069,7 @@
               ++num_generate_bid_calls;
               if (num_generate_bid_calls == kNumGenerateBidCalls)
                 run_loop.Quit();
-            }));
+            })));
 
     // Send one request at a time.
     bidder_worklet->SendPendingSignalsRequests();
@@ -2329,7 +2400,7 @@
                         CreateBasicGenerateBidScript());
 
   auto bidder_worklet = CreateWorklet();
-  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  GenerateBidExpectingNeverCompletes(bidder_worklet.get());
   EXPECT_EQ(
       "Failed to load https://foo.test/helper.wasm "
       "HTTP status = 404 Not Found.",
@@ -2348,7 +2419,7 @@
                         CreateBasicGenerateBidScript());
 
   auto bidder_worklet = CreateWorklet();
-  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  GenerateBidExpectingNeverCompletes(bidder_worklet.get());
   EXPECT_EQ(
       "https://foo.test/helper.wasm Uncaught CompileError: "
       "WasmModuleObject::Compile(): expected magic word 00 61 73 6d, found "
@@ -2473,7 +2544,7 @@
       GenerateBid(bidder_worklet.get());
     } else {
       // On error, the pipe is closed without invoking the callback.
-      GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+      GenerateBidExpectingNeverCompletes(bidder_worklet.get());
     }
 
     for (Event ev : test.events) {
@@ -3460,7 +3531,7 @@
   auto worklet =
       CreateWorklet(interest_group_bidding_url_,
                     /*pause_for_debugger_on_start=*/true, &worklet_impl);
-  GenerateBidExpectingCallbackNotInvoked(worklet.get());
+  GenerateBidExpectingNeverCompletes(worklet.get());
   int id = worklet_impl->context_group_id_for_testing();
   TestChannel* channel = inspector_support.ConnectDebuggerSession(id);
 
diff --git a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
index 2965ef7..fcd4f1ee 100644
--- a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
@@ -87,6 +87,57 @@
   mojo_base.mojom.TimeDelta bid_duration;
 };
 
+// Single use client for each GenerateBid() call.
+interface GenerateBidClient {
+  // Invoked once GenerateBid completes, either having successfully generated a
+  // bid, or having failed to generate one for any reason.
+  //
+  // `bid` If the worklet is successfully loaded and chooses to bid in the
+  //  auction, contains information about the bid. Null otherwise.
+  //
+  // `bidding_signals_data_version` The value of the Data-Version header served
+  //  with the trusted bidding signals.
+  //
+  // `has_bidding_signals_data_version` True to indicate Data-Version header
+  //  was present in the HTTP response for the trusted bidding signals.
+  //  TODO(https://crbug.com/657632): Update when optional integers supported.
+  //
+  // `debug_loss_report_url` The URL to request if the auction runs to
+  //  completion and this bid does not win. All bidders are allowed to send loss
+  //  report, including those who does not bid. This field has the debug prefix
+  //  because it's part of an interim reporting API that will be replaced with
+  //  standardized reporting APIs once available. It must be a valid HTTPS URL.
+  //
+  // `debug_win_report_url` The URL to request if the auction runs to completion
+  //  and this bid wins. This field has the debug prefix because it's part of an
+  //  interim reporting API that will be replaced with standardized reporting
+  //  APIs once available. It must be a valid HTTPS URL.
+  //
+  // `set_priority` The value of the updated priority for this interest group.
+  //  This priority should be applied to the interest group after the auction.
+  //
+  // `has_set_priority` True to indicate the worklet has requested the an
+  //  update to the interest group priority.
+  //  TODO(https://crbug.com/657632): Update when optional integers supported.
+  //
+  // `pa_requests` The various requests made to the Private Aggregation API.
+  //
+  // `errors` The various error messages to be used for debugging. These are too
+  //  sensitive for the renderer to see. There may be errors even when a bid
+  //  is offered, and there may be no errors when there's no bid. Includes
+  //  errors from failing to load the worklet script.
+  OnGenerateBidComplete(
+      BidderWorkletBid? bid,
+      uint32 bidding_signals_data_version,
+      bool has_bidding_signals_data_version,
+      url.mojom.Url? debug_loss_report_url,
+      url.mojom.Url? debug_win_report_url,
+      double set_priority,
+      bool has_set_priority,
+      array<PrivateAggregationRequest> pa_requests,
+      array<string> errors);
+};
+
 // Manages the auction workflow for one loaded FLEDGE bidder worklet.
 // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md
 //
@@ -138,41 +189,8 @@
   // `trace_id` ID of a nestable asynchronous trace event of category `fledge`
   //  to use with tracing calls.
   //
-  // Returns:
-  // `bid` If the worklet is successfully loaded and chooses to bid in the
-  //  auction, contains information about the bid. Null otherwise.
-  //
-  // `bidding_signals_data_version` The value of the Data-Version header served
-  //  with the trusted bidding signals.
-  //
-  // `has_bidding_signals_data_version` True to indicate Data-Version header
-  //  was present in the HTTP response for the trusted bidding signals.
-  //  TODO(https://crbug.com/657632): Update when optional integers supported.
-  //
-  // `debug_loss_report_url` The URL to request if the auction runs to
-  //  completion and this bid does not win. All bidders are allowed to send loss
-  //  report, including those who does not bid. This field has the debug prefix
-  //  because it's part of an interim reporting API that will be replaced with
-  //  standardized reporting APIs once available. It must be a valid HTTPS URL.
-  //
-  // `debug_win_report_url` The URL to request if the auction runs to completion
-  //  and this bid wins. This field has the debug prefix because it's part of an
-  //  interim reporting API that will be replaced with standardized reporting
-  //  APIs once available. It must be a valid HTTPS URL.
-  //
-  // `set_priority` The value of the updated priority for this interest group.
-  //  This priority should be applied to the interest group after the auction.
-  //
-  // `has_set_priority` True to indicate the worklet has requested the an
-  //  update to the interest group priority.
-  //  TODO(https://crbug.com/657632): Update when optional integers supported.
-  //
-  // `pa_requests` The various requests made to the Private Aggregation API.
-  //
-  // `errors` The various error messages to be used for debugging. These are too
-  //  sensitive for the renderer to see. There may be errors even when a bid
-  //  is offered, and there may be no errors when there's no bid. Includes
-  //  errors from failing to load the worklet script.
+  // `generate_bid_client` On completion, its OnGenerateBidComplete()
+  //  method is called. Associated to retain priority ordering of calls.
   GenerateBid(
       BidderWorkletNonSharedParams bidder_worklet_non_shared_params,
       url.mojom.Origin interest_group_join_origin,
@@ -183,16 +201,8 @@
       url.mojom.Origin? browser_signal_top_level_seller_origin,
       BiddingBrowserSignals bidding_browser_signals,
       mojo_base.mojom.Time auction_start_time,
-      uint64 trace_id) => (
-          BidderWorkletBid? bid,
-          uint32 bidding_signals_data_version,
-          bool has_bidding_signals_data_version,
-          url.mojom.Url? debug_loss_report_url,
-          url.mojom.Url? debug_win_report_url,
-          double set_priority,
-          bool has_set_priority,
-          array<PrivateAggregationRequest> pa_requests,
-          array<string> errors);
+      uint64 trace_id,
+      pending_associated_remote<GenerateBidClient> generate_bid_client);
 
   // Sends pending bidding signals URL requests, if any. Unlike with
   // SellerWorklets, this must be called for BidderWorklets that need to send
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index ff52eb31..e20dd32 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2027,6 +2027,7 @@
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/audio_output.shard.test-cml",
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
       "//build/config/fuchsia/test/present_view.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
@@ -2580,6 +2581,7 @@
 
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 649622a..a0a859b 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -289,6 +289,7 @@
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
 
     additional_manifest_fragments = [
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/fuchsia_web/runners/BUILD.gn b/fuchsia_web/runners/BUILD.gn
index deb2982..d17150c 100644
--- a/fuchsia_web/runners/BUILD.gn
+++ b/fuchsia_web/runners/BUILD.gn
@@ -325,6 +325,7 @@
   test_runner_shard =
       "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
   additional_manifest_fragments = [
+    "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
     "//build/config/fuchsia/test/network.shard.test-cml",
     "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
   ]
diff --git a/fuchsia_web/runners/cast/cast_runner.cml b/fuchsia_web/runners/cast/cast_runner.cml
index 83c6727..1ebf567 100644
--- a/fuchsia_web/runners/cast/cast_runner.cml
+++ b/fuchsia_web/runners/cast/cast_runner.cml
@@ -57,6 +57,7 @@
         "fuchsia.hwinfo.Product",
         "fuchsia.input.virtualkeyboard.ControllerCreator",
         "fuchsia.intl.PropertyProvider",
+        "fuchsia.kernel.VmexResource",
         // "fuchsia.legacymetrics.MetricsRecorder",
         // "fuchsia.media.Audio",
         "fuchsia.media.AudioDeviceEnumerator",
diff --git a/fuchsia_web/runners/cast/cast_runner.cmx b/fuchsia_web/runners/cast/cast_runner.cmx
index eaf7f8b..5a5c7848 100644
--- a/fuchsia_web/runners/cast/cast_runner.cmx
+++ b/fuchsia_web/runners/cast/cast_runner.cmx
@@ -18,6 +18,7 @@
       "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
+      "fuchsia.kernel.VmexResource",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
       "fuchsia.media.AudioDeviceEnumerator",
diff --git a/fuchsia_web/runners/web/web_runner.cmx b/fuchsia_web/runners/web/web_runner.cmx
index c98e487..7903f745 100644
--- a/fuchsia_web/runners/web/web_runner.cmx
+++ b/fuchsia_web/runners/web/web_runner.cmx
@@ -17,6 +17,7 @@
       "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
+      "fuchsia.kernel.VmexResource",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
       "fuchsia.media.AudioDeviceEnumerator",
diff --git a/fuchsia_web/shell/cast_streaming_shell.cmx b/fuchsia_web/shell/cast_streaming_shell.cmx
index a61faf6f..a2ebfd7 100644
--- a/fuchsia_web/shell/cast_streaming_shell.cmx
+++ b/fuchsia_web/shell/cast_streaming_shell.cmx
@@ -16,6 +16,7 @@
       "fuchsia.feedback.CrashReportingProductRegister",
       "fuchsia.fonts.Provider",
       "fuchsia.intl.PropertyProvider",
+      "fuchsia.kernel.VmexResource",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
       "fuchsia.media.AudioDeviceEnumerator",
diff --git a/fuchsia_web/shell/web_engine_shell.cmx b/fuchsia_web/shell/web_engine_shell.cmx
index a14d635..61ead3f 100644
--- a/fuchsia_web/shell/web_engine_shell.cmx
+++ b/fuchsia_web/shell/web_engine_shell.cmx
@@ -19,6 +19,7 @@
       "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
+      "fuchsia.kernel.VmexResource",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
       "fuchsia.media.AudioDeviceEnumerator",
diff --git a/fuchsia_web/webengine/web_instance.cml b/fuchsia_web/webengine/web_instance.cml
index 98c32a07..f018d92a 100644
--- a/fuchsia_web/webengine/web_instance.cml
+++ b/fuchsia_web/webengine/web_instance.cml
@@ -44,6 +44,7 @@
         "fuchsia.fonts.Provider",
         "fuchsia.hwinfo.Product",
         "fuchsia.intl.PropertyProvider",
+        "fuchsia.kernel.VmexResource",
         "fuchsia.memorypressure.Provider",
         "fuchsia.process.Launcher",
         "fuchsia.sysmem.Allocator",
diff --git a/fuchsia_web/webengine/web_instance.cmx b/fuchsia_web/webengine/web_instance.cmx
index 4163a99..be893808 100644
--- a/fuchsia_web/webengine/web_instance.cmx
+++ b/fuchsia_web/webengine/web_instance.cmx
@@ -18,6 +18,7 @@
       "fuchsia.hwinfo.Product",
       "fuchsia.input.virtualkeyboard.ControllerCreator",
       "fuchsia.intl.PropertyProvider",
+      "fuchsia.kernel.VmexResource",
       "fuchsia.legacymetrics.MetricsRecorder",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
diff --git a/fuchsia_web/webinstance_host/web_instance_host.cc b/fuchsia_web/webinstance_host/web_instance_host.cc
index ca611b1..2d2f98b 100644
--- a/fuchsia_web/webinstance_host/web_instance_host.cc
+++ b/fuchsia_web/webinstance_host/web_instance_host.cc
@@ -370,12 +370,13 @@
   // at:
   //   https://fuchsia.dev/reference/fidl/fuchsia.web#CreateContextParams.service_directory
   std::vector<std::string> services{
-      "fuchsia.buildinfo.Provider",      "fuchsia.device.NameProvider",
-      "fuchsia.fonts.Provider",          "fuchsia.hwinfo.Product",
-      "fuchsia.intl.PropertyProvider",   "fuchsia.logger.LogSink",
-      "fuchsia.memorypressure.Provider", "fuchsia.process.Launcher",
+      "fuchsia.buildinfo.Provider",    "fuchsia.device.NameProvider",
+      "fuchsia.fonts.Provider",        "fuchsia.hwinfo.Product",
+      "fuchsia.intl.PropertyProvider", "fuchsia.kernel.VmexResource",
+      "fuchsia.logger.LogSink",        "fuchsia.memorypressure.Provider",
+      "fuchsia.process.Launcher",
       "fuchsia.settings.Display",  // Used if preferred theme is DEFAULT.
-      "fuchsia.sysmem.Allocator",        "fuchsia.ui.scenic.Scenic"};
+      "fuchsia.sysmem.Allocator",      "fuchsia.ui.scenic.Scenic"};
 
   // TODO(crbug.com/1209031): Provide these conditionally, once corresponding
   // ContextFeatureFlags have been defined.
diff --git a/gin/BUILD.gn b/gin/BUILD.gn
index ed2b52f..0c02641 100644
--- a/gin/BUILD.gn
+++ b/gin/BUILD.gn
@@ -198,5 +198,7 @@
     # TODO(https://crbug.com/1185811): Investigate removing the requirement for
     # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests.
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
+    additional_manifest_fragments =
+        [ "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml" ]
   }
 }
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index a9d6866..2af2493 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -832,6 +832,7 @@
 
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index 62fd40ab..9f983fef 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -712,15 +712,8 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
       performAction:grey_typeText(@"this is a test")];
 
-  if ([ChromeEarlGrey isExperimentalOmniboxEnabled]) {
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
-        performAction:grey_tap()];
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
-        performAction:grey_longPress()];
-  } else {
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
-        performAction:grey_tap()];
-  }
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
+      performAction:grey_tap()];
 
   // Pressing should allow select and selectAll.
   // Wait for UIMenuController to appear or timeout after 2 seconds.
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm
index 9c7ef8fc..7caa970 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_experimental.mm
@@ -492,6 +492,12 @@
     [self acceptAutocompleteText];
   } else {
     [self becomeFirstResponder];
+    UIMenuController* menuController = [UIMenuController sharedMenuController];
+    if (menuController.isMenuVisible) {
+      [menuController hideMenu];
+    } else {
+      [menuController showMenuFromView:self rect:self.bounds];
+    }
   }
 }
 
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 141337cb..3c622a05 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ba89d623f854c4d99c3a54384c6a4957f29096dd
\ No newline at end of file
+efa0c0eebae6942a02964c7cf482a4ed4a7e3b2a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 9eb37fc8..969f22af 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9bb931ee4fa49a36302d8e6e75844f035cbee36d
\ No newline at end of file
+1ccd5df11f4570bfce8bb8217891712a02f6294b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index a96e493..c53fc9e 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0943fa7b76a839c42ffd3ae7033d15e5c28d431c
\ No newline at end of file
+0bffa95c454c08afa4a4f45ce9a02d002b2bd179
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index baf454d..5f89b49 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-0de1e8d4aff4380c3a5f0f590b1db915db9b0549
\ No newline at end of file
+3af08f4429cd4d43f1dde3f91e25eba6cd8e8147
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index b80c384..b178199 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f343f66b37e7871abf287c8bfad5034e9b828de9
\ No newline at end of file
+e468aa4f006ab935bc9bd3edd71336d7c9d55bf6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 3b45008..7739934 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-e611d37a91a9d0f2b04b2768a513a6b27659792a
\ No newline at end of file
+1110f7e96e1aff6a555c1a1c6b7a2127996ff918
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 28a709be..cd73652 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f2fc285c58325e31ef19a76b462328805763e622
\ No newline at end of file
+05758d525e85d31d3a5a9d552dad8e991bb321f1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 735e53a..2fe603b 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a21885846ead3864755c22e0150fe09dba2ae338
\ No newline at end of file
+97ee4087024faa1aa3c8820d3a6113f23e09c9bd
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 12214aa..de6d59e 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-343712063b0c3630f89afdebe687008ced395338
\ No newline at end of file
+5344966552648b2a67fa401de7e3b90ee602587f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index a892df1b..3e5d4d76 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-46279f610967a0866756f9d6a2eed98712553b59
\ No newline at end of file
+78008e6ccb24cfa6ede586ec69f76985fc66ab16
\ No newline at end of file
diff --git a/media/BUILD.gn b/media/BUILD.gn
index bc88823..d6a29c4 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -227,6 +227,7 @@
 
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/audio_output.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/platform_video_codecs.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn
index 32bd639..ee2fc71 100644
--- a/mojo/core/BUILD.gn
+++ b/mojo/core/BUILD.gn
@@ -62,6 +62,7 @@
       "invitation_dispatcher.h",
       "ipcz_api.h",
       "ipcz_driver/driver.h",
+      "ipcz_driver/shared_buffer.h",
       "ipcz_driver/transmissible_platform_handle.h",
       "ipcz_driver/transport.h",
       "ipcz_driver/wrapped_platform_handle.h",
@@ -99,6 +100,9 @@
       "ipcz_driver/mojo_trap.h",
       "ipcz_driver/object.cc",
       "ipcz_driver/object.h",
+      "ipcz_driver/shared_buffer.cc",
+      "ipcz_driver/shared_buffer_mapping.cc",
+      "ipcz_driver/shared_buffer_mapping.h",
       "ipcz_driver/transmissible_platform_handle.cc",
       "ipcz_driver/transport.cc",
       "ipcz_driver/wrapped_platform_handle.cc",
diff --git a/mojo/core/core_ipcz.cc b/mojo/core/core_ipcz.cc
index 1af155b..bc8bb18 100644
--- a/mojo/core/core_ipcz.cc
+++ b/mojo/core/core_ipcz.cc
@@ -6,16 +6,24 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <map>
+#include <tuple>
 #include <utility>
 #include <vector>
 
 #include "base/containers/span.h"
+#include "base/memory/platform_shared_memory_region.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/synchronization/lock.h"
 #include "base/time/time.h"
 #include "mojo/core/ipcz_api.h"
 #include "mojo/core/ipcz_driver/mojo_trap.h"
+#include "mojo/core/ipcz_driver/shared_buffer.h"
+#include "mojo/core/ipcz_driver/shared_buffer_mapping.h"
 #include "mojo/core/ipcz_driver/wrapped_platform_handle.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
 #include "third_party/ipcz/include/ipcz/ipcz.h"
@@ -137,6 +145,38 @@
   std::vector<IpczHandle> handles_;
 };
 
+// Tracks active Mojo memory mappings by base address, since that's how the Mojo
+// API identifies them for unmapping.
+class MappingTable {
+ public:
+  MappingTable() = default;
+  ~MappingTable() = default;
+
+  void Add(scoped_refptr<ipcz_driver::SharedBufferMapping> mapping) {
+    base::AutoLock lock(lock_);
+    void* address = mapping->memory();
+    mappings_.emplace(address, std::move(mapping));
+  }
+
+  MojoResult Remove(void* address) {
+    base::AutoLock lock(lock_);
+    if (!mappings_.erase(address)) {
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    }
+    return MOJO_RESULT_OK;
+  }
+
+ private:
+  base::Lock lock_;
+  std::map<void*, scoped_refptr<ipcz_driver::SharedBufferMapping>> mappings_
+      GUARDED_BY(lock_);
+};
+
+MappingTable& GetMappingTable() {
+  static base::NoDestructor<MappingTable> table;
+  return *table;
+}
+
 // ipcz get and put operations differ slightly in their return code semantics as
 // compared to Mojo read and write operations. These helpers perform the
 // translation.
@@ -406,14 +446,37 @@
     uint64_t num_bytes,
     const MojoCreateSharedBufferOptions* options,
     MojoHandle* shared_buffer_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto region =
+      base::subtle::PlatformSharedMemoryRegion::CreateWritable(num_bytes);
+  if (!region.IsValid()) {
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  *shared_buffer_handle =
+      ipcz_driver::SharedBuffer::MakeBoxed(std::move(region));
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoDuplicateBufferHandleIpcz(
     MojoHandle buffer_handle,
     const MojoDuplicateBufferHandleOptions* options,
     MojoHandle* new_buffer_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto* buffer = ipcz_driver::SharedBuffer::FromBox(buffer_handle);
+  if (!buffer || !new_buffer_handle ||
+      (options && options->struct_size < sizeof(*options))) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  const bool read_only =
+      options &&
+      (options->flags & MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY) != 0;
+  auto [new_buffer, result] = buffer->Duplicate(read_only);
+  if (result != IPCZ_RESULT_OK) {
+    return result;
+  }
+
+  *new_buffer_handle = ipcz_driver::SharedBuffer::Box(std::move(new_buffer));
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoMapBufferIpcz(MojoHandle buffer_handle,
@@ -421,17 +484,35 @@
                              uint64_t num_bytes,
                              const MojoMapBufferOptions* options,
                              void** address) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto* buffer = ipcz_driver::SharedBuffer::FromBox(buffer_handle);
+  if (!buffer) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  auto mapping = ipcz_driver::SharedBufferMapping::Create(
+      buffer->region(), static_cast<size_t>(offset),
+      static_cast<size_t>(num_bytes));
+  if (!mapping) {
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+  *address = mapping->memory();
+  GetMappingTable().Add(std::move(mapping));
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoUnmapBufferIpcz(void* address) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  return GetMappingTable().Remove(address);
 }
 
 MojoResult MojoGetBufferInfoIpcz(MojoHandle buffer_handle,
                                  const MojoGetBufferInfoOptions* options,
                                  MojoSharedBufferInfo* info) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto* buffer = ipcz_driver::SharedBuffer::FromBox(buffer_handle);
+  if (!buffer || !info || info->struct_size < sizeof(*info)) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  info->size = buffer->region().GetSize();
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoCreateTrapIpcz(MojoTrapEventHandler handler,
@@ -517,7 +598,18 @@
     MojoPlatformSharedMemoryRegionAccessMode access_mode,
     const MojoWrapPlatformSharedMemoryRegionOptions* options,
     MojoHandle* mojo_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  if (!platform_handles || !num_bytes || !guid || !mojo_handle) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  auto buffer = ipcz_driver::SharedBuffer::CreateForMojoWrapper(
+      base::make_span(platform_handles, num_platform_handles), num_bytes, *guid,
+      access_mode);
+  if (!buffer) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  *mojo_handle = ipcz_driver::SharedBuffer::Box(std::move(buffer));
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoUnwrapPlatformSharedMemoryRegionIpcz(
@@ -528,7 +620,70 @@
     uint64_t* num_bytes,
     MojoSharedBufferGuid* mojo_guid,
     MojoPlatformSharedMemoryRegionAccessMode* access_mode) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  if (!mojo_handle || !platform_handles || !num_platform_handles ||
+      !mojo_guid) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  auto* buffer = ipcz_driver::SharedBuffer::FromBox(mojo_handle);
+  if (!buffer) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  using Mode = base::subtle::PlatformSharedMemoryRegion::Mode;
+  const Mode mode = buffer->region().GetMode();
+  const base::UnguessableToken guid = buffer->region().GetGUID();
+  const uint32_t size = static_cast<uint32_t>(buffer->region().GetSize());
+
+  uint32_t capacity = *num_platform_handles;
+  uint32_t required_handles = 1;
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
+  if (buffer->region().GetMode() ==
+      base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
+    required_handles = 2;
+  }
+#endif
+  *num_platform_handles = required_handles;
+  if (capacity < required_handles) {
+    return MOJO_RESULT_RESOURCE_EXHAUSTED;
+  }
+
+  PlatformHandle handles[2];
+  base::subtle::ScopedPlatformSharedMemoryHandle region_handle =
+      buffer->region().PassPlatformHandle();
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
+  handles[0] = PlatformHandle(std::move(region_handle.fd));
+  handles[1] = PlatformHandle(std::move(region_handle.readonly_fd));
+#else
+  handles[0] = PlatformHandle(std::move(region_handle));
+#endif
+
+  for (size_t i = 0; i < required_handles; ++i) {
+    PlatformHandle::ToMojoPlatformHandle(std::move(handles[i]),
+                                         &platform_handles[i]);
+  }
+
+  *num_bytes = size;
+  mojo_guid->high = guid.GetHighForSerialization();
+  mojo_guid->low = guid.GetLowForSerialization();
+
+  switch (mode) {
+    case Mode::kReadOnly:
+      *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
+      break;
+    case Mode::kWritable:
+      *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE;
+      break;
+    case Mode::kUnsafe:
+      *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE;
+      break;
+    default:
+      *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
+      NOTREACHED();
+  }
+
+  std::ignore = ipcz_driver::SharedBuffer::Unbox(mojo_handle);
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoCreateInvitationIpcz(const MojoCreateInvitationOptions* options,
diff --git a/mojo/core/core_ipcz_test.cc b/mojo/core/core_ipcz_test.cc
index 51af520..23ae69e0 100644
--- a/mojo/core/core_ipcz_test.cc
+++ b/mojo/core/core_ipcz_test.cc
@@ -49,6 +49,28 @@
     return message;
   }
 
+  // Unwraps and re-wraps a Mojo shared buffer handle, extracting some of its
+  // serialized details in the process.
+  struct SharedBufferDetails {
+    uint64_t size;
+    MojoPlatformSharedMemoryRegionAccessMode mode;
+  };
+  SharedBufferDetails PeekSharedBuffer(MojoHandle& buffer) {
+    SharedBufferDetails details;
+    uint32_t num_platform_handles = 2;
+    MojoPlatformHandle platform_handles[2];
+    MojoSharedBufferGuid guid;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mojo().UnwrapPlatformSharedMemoryRegion(
+                  buffer, nullptr, platform_handles, &num_platform_handles,
+                  &details.size, &guid, &details.mode));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mojo().WrapPlatformSharedMemoryRegion(
+                  platform_handles, num_platform_handles, details.size, &guid,
+                  details.mode, nullptr, &buffer));
+    return details;
+  }
+
  private:
   const MojoSystemThunks2* const mojo_{GetMojoIpczImpl()};
 };
@@ -321,5 +343,105 @@
   listener.WaitForPeerClosure();
 }
 
+TEST_F(CoreIpczTest, BasicSharedBuffer) {
+  const base::StringPiece kContents = "steamed hams";
+  MojoHandle buffer;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().CreateSharedBuffer(kContents.size(), nullptr, &buffer));
+
+  // New Mojo shared buffers are always writable by default.
+  SharedBufferDetails details = PeekSharedBuffer(buffer);
+  EXPECT_EQ(MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE,
+            details.mode);
+  EXPECT_EQ(kContents.size(), details.size);
+
+  void* address;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().MapBuffer(buffer, 0, kContents.size(), nullptr, &address));
+  memcpy(address, kContents.data(), kContents.size());
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().UnmapBuffer(address));
+  address = nullptr;
+
+  // We can duplicate to handle which can only be mapped for reading.
+  MojoHandle readonly_buffer;
+  const MojoDuplicateBufferHandleOptions readonly_options = {
+      .struct_size = sizeof(readonly_options),
+      .flags = MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY,
+  };
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().DuplicateBufferHandle(
+                                buffer, &readonly_options, &readonly_buffer));
+
+  // With a read-only duplicate, it should now be impossible to create a
+  // writable duplicate, and the original buffer handle should now be in
+  // read-only mode.
+  MojoHandle writable_buffer;
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mojo().DuplicateBufferHandle(buffer, nullptr, &writable_buffer));
+
+  details = PeekSharedBuffer(buffer);
+  EXPECT_EQ(MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY,
+            details.mode);
+  EXPECT_EQ(kContents.size(), details.size);
+
+  details = PeekSharedBuffer(readonly_buffer);
+  EXPECT_EQ(MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY,
+            details.mode);
+  EXPECT_EQ(kContents.size(), details.size);
+
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(buffer));
+
+  // Additional read-only duplicates are OK though.
+  MojoHandle dupe;
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().DuplicateBufferHandle(
+                                readonly_buffer, &readonly_options, &dupe));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(dupe));
+
+  // And finally we can map the buffer again to find the same contents.
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().MapBuffer(readonly_buffer, 0, kContents.size(), nullptr,
+                             &address));
+  EXPECT_EQ(kContents, base::StringPiece(static_cast<const char*>(address),
+                                         kContents.size()));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(readonly_buffer));
+}
+
+TEST_F(CoreIpczTest, SharedBufferDuplicateUnsafe) {
+  // A buffer which has been duplicated at least once without READ_ONLY can
+  // never be duplicated as read-only.
+  constexpr size_t kSize = 64;
+  MojoHandle buffer;
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().CreateSharedBuffer(kSize, nullptr, &buffer));
+
+  MojoHandle dupe;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().DuplicateBufferHandle(buffer, nullptr, &dupe));
+
+  SharedBufferDetails details = PeekSharedBuffer(buffer);
+  EXPECT_EQ(MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE,
+            details.mode);
+  EXPECT_EQ(kSize, details.size);
+
+  details = PeekSharedBuffer(dupe);
+  EXPECT_EQ(MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE,
+            details.mode);
+  EXPECT_EQ(kSize, details.size);
+
+  MojoHandle readonly_dupe;
+  MojoDuplicateBufferHandleOptions options = {
+      .struct_size = sizeof(options),
+      .flags = MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY,
+  };
+  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+            mojo().DuplicateBufferHandle(buffer, &options, &readonly_dupe));
+
+  // Unsafe duplication is still possible though.
+  MojoHandle unsafe_dupe;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().DuplicateBufferHandle(buffer, nullptr, &unsafe_dupe));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(unsafe_dupe));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(dupe));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(buffer));
+}
+
 }  // namespace
 }  // namespace mojo::core
diff --git a/mojo/core/ipcz_driver/object.h b/mojo/core/ipcz_driver/object.h
index aed677f..a702ecf 100644
--- a/mojo/core/ipcz_driver/object.h
+++ b/mojo/core/ipcz_driver/object.h
@@ -85,6 +85,9 @@
     return object;
   }
 
+  // Boxes a reference to `object` and returns an IpczHandle for the box.
+  static IpczHandle Box(scoped_refptr<ObjectBase> object);
+
   // Closes this object.
   virtual void Close();
 
@@ -108,9 +111,6 @@
  protected:
   virtual ~ObjectBase();
 
-  // Boxes a reference to `object` and returns an IpczHandle for the box.
-  static IpczHandle Box(scoped_refptr<ObjectBase> object);
-
   // Peeks at `box` and returns its underlying driver handle.
   static IpczDriverHandle PeekBox(IpczHandle box);
 
diff --git a/mojo/core/ipcz_driver/shared_buffer.cc b/mojo/core/ipcz_driver/shared_buffer.cc
new file mode 100644
index 0000000..84779f0
--- /dev/null
+++ b/mojo/core/ipcz_driver/shared_buffer.cc
@@ -0,0 +1,266 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/core/ipcz_driver/shared_buffer.h"
+
+#include <cstdint>
+#include <utility>
+
+#include "base/files/scoped_file.h"
+#include "base/memory/ref_counted.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+#include "third_party/ipcz/include/ipcz/ipcz.h"
+
+namespace mojo::core::ipcz_driver {
+
+namespace {
+
+// Enumeration of supported region access modes.
+enum class BufferMode : uint32_t {
+  kReadOnly,
+  kWritable,
+  kUnsafe,
+};
+
+// The wire representation of a serialized shared buffer.
+struct IPCZ_ALIGN(8) BufferHeader {
+  // The size of this structure, in bytes. Used for versioning.
+  uint32_t size;
+
+  // The size of the shared memory buffer.
+  uint32_t buffer_size;
+
+  // Access mode for the region.
+  BufferMode mode;
+
+  // The low and high components of the 128-bit GUID used to identify this
+  // buffer.
+  uint64_t guid_low;
+  uint64_t guid_high;
+};
+
+// Produces a ScopedPlatformSharedMemoryHandle from a set of PlatformHandles and
+// an access mode.
+base::subtle::ScopedPlatformSharedMemoryHandle
+CreateRegionHandleFromPlatformHandles(
+    base::span<PlatformHandle> handles,
+    base::subtle::PlatformSharedMemoryRegion::Mode mode) {
+  if (handles.empty()) {
+    return {};
+  }
+
+#if BUILDFLAG(IS_WIN)
+  return handles[0].TakeHandle();
+#elif BUILDFLAG(IS_FUCHSIA)
+  return zx::vmo(handles[0].TakeHandle());
+#elif BUILDFLAG(IS_APPLE)
+  return handles[0].TakeMachSendRight();
+#elif BUILDFLAG(IS_ANDROID)
+  return handles[0].TakeFD();
+#else
+  base::ScopedFD readonly_fd;
+  if (mode == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
+    if (handles.size() < 2) {
+      return {};
+    }
+    readonly_fd = handles[1].TakeFD();
+  }
+  base::ScopedFD fd = handles[0].TakeFD();
+  return base::subtle::ScopedFDPair(std::move(fd), std::move(readonly_fd));
+#endif
+}
+
+}  // namespace
+
+SharedBuffer::SharedBuffer(base::subtle::PlatformSharedMemoryRegion region)
+    : region_(std::move(region)) {}
+
+SharedBuffer::~SharedBuffer() = default;
+
+std::pair<scoped_refptr<SharedBuffer>, IpczResult> SharedBuffer::Duplicate(
+    bool read_only) {
+  using Mode = base::subtle::PlatformSharedMemoryRegion::Mode;
+  if (region_.GetMode() == Mode::kWritable) {
+    if (read_only && !region_.ConvertToReadOnly()) {
+      return {nullptr, MOJO_RESULT_RESOURCE_EXHAUSTED};
+    } else if (!read_only && !region_.ConvertToUnsafe()) {
+      return {nullptr, MOJO_RESULT_RESOURCE_EXHAUSTED};
+    }
+  }
+
+  const Mode required_mode = read_only ? Mode::kReadOnly : Mode::kUnsafe;
+  if (region_.GetMode() != required_mode) {
+    return {nullptr, MOJO_RESULT_FAILED_PRECONDITION};
+  }
+
+  auto new_region = region_.Duplicate();
+  if (!new_region.IsValid()) {
+    return {nullptr, MOJO_RESULT_RESOURCE_EXHAUSTED};
+  }
+
+  return {
+      base::MakeRefCounted<ipcz_driver::SharedBuffer>(std::move(new_region)),
+      IPCZ_RESULT_OK};
+}
+
+// static
+scoped_refptr<SharedBuffer> SharedBuffer::CreateForMojoWrapper(
+    base::span<const MojoPlatformHandle> mojo_platform_handles,
+    uint32_t size,
+    const MojoSharedBufferGuid& mojo_guid,
+    MojoPlatformSharedMemoryRegionAccessMode access) {
+  if (mojo_platform_handles.empty() || mojo_platform_handles.size() > 2) {
+    return nullptr;
+  }
+
+  using Mode = base::subtle::PlatformSharedMemoryRegion::Mode;
+  Mode mode;
+  switch (access) {
+    case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY:
+      mode = Mode::kReadOnly;
+      break;
+    case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE:
+      mode = Mode::kWritable;
+      break;
+    case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE:
+      mode = Mode::kUnsafe;
+      break;
+    default:
+      return nullptr;
+  }
+
+  auto guid =
+      base::UnguessableToken::Deserialize(mojo_guid.high, mojo_guid.low);
+  if (guid.is_empty()) {
+    return nullptr;
+  }
+
+  PlatformHandle handles[2];
+  for (size_t i = 0; i < mojo_platform_handles.size(); ++i) {
+    handles[i] =
+        PlatformHandle::FromMojoPlatformHandle(&mojo_platform_handles[i]);
+  }
+
+  auto handle = CreateRegionHandleFromPlatformHandles(
+      {&handles[0], mojo_platform_handles.size()}, mode);
+  auto region = base::subtle::PlatformSharedMemoryRegion::Take(
+      std::move(handle), mode, size, guid);
+  if (!region.IsValid()) {
+    return nullptr;
+  }
+
+  return base::MakeRefCounted<SharedBuffer>(std::move(region));
+}
+
+void SharedBuffer::Close() {
+  region_ = {};
+}
+
+bool SharedBuffer::IsSerializable() const {
+  return true;
+}
+
+bool SharedBuffer::GetSerializedDimensions(Transport& transmitter,
+                                           size_t& num_bytes,
+                                           size_t& num_handles) {
+  num_bytes = sizeof(BufferHeader);
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA) || \
+    BUILDFLAG(IS_ANDROID)
+  num_handles = 1;
+#else
+  if (region_.GetMode() ==
+      base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
+    num_handles = 2;
+  } else {
+    num_handles = 1;
+  }
+#endif
+  return true;
+}
+
+bool SharedBuffer::Serialize(Transport& transmitter,
+                             base::span<uint8_t> data,
+                             base::span<PlatformHandle> handles) {
+  if (!region_.IsValid()) {
+    return false;
+  }
+
+  DCHECK_GE(data.size(), sizeof(BufferHeader));
+  BufferHeader& header = *reinterpret_cast<BufferHeader*>(data.data());
+  header.size = static_cast<uint32_t>(region_.GetSize());
+  switch (region_.GetMode()) {
+    case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly:
+      header.mode = BufferMode::kReadOnly;
+      break;
+    case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable:
+      header.mode = BufferMode::kWritable;
+      break;
+    case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe:
+      header.mode = BufferMode::kUnsafe;
+      break;
+  }
+  base::UnguessableToken guid = region_.GetGUID();
+  header.guid_low = guid.GetLowForSerialization();
+  header.guid_high = guid.GetHighForSerialization();
+
+  auto handle = region_.PassPlatformHandle();
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA) || \
+    BUILDFLAG(IS_ANDROID)
+  DCHECK_EQ(handles.size(), 1u);
+  handles[0] = PlatformHandle(std::move(handle));
+#else
+  if (header.mode == BufferMode::kWritable) {
+    DCHECK_EQ(2u, handles.size());
+    handles[0] = PlatformHandle(std::move(handle.fd));
+    handles[1] = PlatformHandle(std::move(handle.readonly_fd));
+  } else {
+    DCHECK_EQ(1u, handles.size());
+    handles[0] = PlatformHandle(std::move(handle.fd));
+  }
+#endif
+
+  return true;
+}
+
+// static
+scoped_refptr<SharedBuffer> SharedBuffer::Deserialize(
+    base::span<const uint8_t> data,
+    base::span<PlatformHandle> handles) {
+  if (data.size() < sizeof(BufferHeader) || handles.empty()) {
+    return nullptr;
+  }
+
+  const BufferHeader& header =
+      *reinterpret_cast<const BufferHeader*>(data.data());
+  base::subtle::PlatformSharedMemoryRegion::Mode mode;
+  switch (header.mode) {
+    case BufferMode::kReadOnly:
+      mode = base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly;
+      break;
+    case BufferMode::kWritable:
+      mode = base::subtle::PlatformSharedMemoryRegion::Mode::kWritable;
+      break;
+    case BufferMode::kUnsafe:
+      mode = base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe;
+      break;
+    default:
+      return nullptr;
+  }
+
+  auto guid =
+      base::UnguessableToken::Deserialize(header.guid_high, header.guid_low);
+
+  auto handle = CreateRegionHandleFromPlatformHandles(handles, mode);
+  auto region = base::subtle::PlatformSharedMemoryRegion::Take(
+      std::move(handle), mode, header.size, guid);
+  if (!region.IsValid()) {
+    return nullptr;
+  }
+
+  return base::MakeRefCounted<SharedBuffer>(std::move(region));
+}
+
+}  // namespace mojo::core::ipcz_driver
diff --git a/mojo/core/ipcz_driver/shared_buffer.h b/mojo/core/ipcz_driver/shared_buffer.h
new file mode 100644
index 0000000..55a53ae
--- /dev/null
+++ b/mojo/core/ipcz_driver/shared_buffer.h
@@ -0,0 +1,70 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_CORE_IPCZ_DRIVER_SHARED_BUFFER_H_
+#define MOJO_CORE_IPCZ_DRIVER_SHARED_BUFFER_H_
+
+#include <cstdint>
+
+#include "base/containers/span.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/scoped_refptr.h"
+#include "mojo/core/ipcz_driver/object.h"
+#include "mojo/core/system_impl_export.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+
+namespace mojo::core::ipcz_driver {
+
+// A SharedBuffer operates like Mojo Core's shared buffer object, as a weird
+// thin wrapper around base's shared memory APIs. This exists to allow those
+// objects to be boxed and transmitted through ipcz portals.
+class MOJO_SYSTEM_IMPL_EXPORT SharedBuffer : public Object<SharedBuffer> {
+ public:
+  explicit SharedBuffer(base::subtle::PlatformSharedMemoryRegion region);
+
+  template <typename RegionType>
+  static scoped_refptr<SharedBuffer> MakeForRegion(RegionType region) {
+    return base::MakeRefCounted<SharedBuffer>(
+        RegionType::TakeHandleForSerialization(std::move(region)));
+  }
+
+  static constexpr Type object_type() { return kSharedBuffer; }
+
+  base::subtle::PlatformSharedMemoryRegion& region() { return region_; }
+
+  // Duplicates this buffer to a new buffer and returns a ref to the new object.
+  // The second return value is a result indicating success or failure. Anything
+  // other than IPCZ_RESULT_OK implies failure, and buffer must be null.
+  std::pair<scoped_refptr<SharedBuffer>, IpczResult> Duplicate(bool read_only);
+
+  // Helper to support the MojoWrapPlatformSharedMemoryRegion API.
+  static scoped_refptr<SharedBuffer> CreateForMojoWrapper(
+      base::span<const MojoPlatformHandle> mojo_platform_handles,
+      uint32_t size,
+      const MojoSharedBufferGuid& mojo_guid,
+      MojoPlatformSharedMemoryRegionAccessMode access);
+
+  // Object:
+  void Close() override;
+  bool IsSerializable() const override;
+  bool GetSerializedDimensions(Transport& transmitter,
+                               size_t& num_bytes,
+                               size_t& num_handles) override;
+  bool Serialize(Transport& transmitter,
+                 base::span<uint8_t> data,
+                 base::span<PlatformHandle> handles) override;
+
+  static scoped_refptr<SharedBuffer> Deserialize(
+      base::span<const uint8_t> data,
+      base::span<PlatformHandle> handles);
+
+ private:
+  ~SharedBuffer() override;
+
+  base::subtle::PlatformSharedMemoryRegion region_;
+};
+
+}  // namespace mojo::core::ipcz_driver
+
+#endif  // MOJO_CORE_IPCZ_DRIVER_SHARED_BUFFER_H_
diff --git a/mojo/core/ipcz_driver/shared_buffer_mapping.cc b/mojo/core/ipcz_driver/shared_buffer_mapping.cc
new file mode 100644
index 0000000..cfbace4
--- /dev/null
+++ b/mojo/core/ipcz_driver/shared_buffer_mapping.cc
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/core/ipcz_driver/shared_buffer_mapping.h"
+
+#include <utility>
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/memory/writable_shared_memory_region.h"
+
+namespace mojo::core::ipcz_driver {
+
+namespace {
+
+// Wraps and unwraps a raw PlatformSharedMemoryRegion as some more specific base
+// region type to produce an appropriate SharedMemoryMapping.
+template <typename RegionType>
+std::unique_ptr<base::SharedMemoryMapping> MapRegion(
+    base::subtle::PlatformSharedMemoryRegion& region,
+    size_t offset,
+    size_t size,
+    void** memory) {
+  auto r = RegionType::Deserialize(std::move(region));
+  typename RegionType::MappingType m = r.MapAt(offset, size);
+  region = RegionType::TakeHandleForSerialization(std::move(r));
+  if (!m.IsValid()) {
+    return nullptr;
+  }
+  *memory = const_cast<void*>(m.memory());
+  return std::make_unique<typename RegionType::MappingType>(std::move(m));
+}
+
+std::unique_ptr<base::SharedMemoryMapping> MapPlatformRegion(
+    base::subtle::PlatformSharedMemoryRegion& region,
+    size_t offset,
+    size_t size,
+    void** memory) {
+  using Mode = base::subtle::PlatformSharedMemoryRegion::Mode;
+  switch (region.GetMode()) {
+    case Mode::kReadOnly:
+      return MapRegion<base::ReadOnlySharedMemoryRegion>(region, offset, size,
+                                                         memory);
+    case Mode::kWritable:
+      return MapRegion<base::WritableSharedMemoryRegion>(region, offset, size,
+                                                         memory);
+    case Mode::kUnsafe:
+      return MapRegion<base::UnsafeSharedMemoryRegion>(region, offset, size,
+                                                       memory);
+  }
+  return nullptr;
+}
+
+}  // namespace
+
+SharedBufferMapping::SharedBufferMapping(
+    std::unique_ptr<base::SharedMemoryMapping> mapping,
+    void* memory)
+    : mapping_(std::move(mapping)), memory_(memory) {}
+
+SharedBufferMapping::~SharedBufferMapping() = default;
+
+// static
+scoped_refptr<SharedBufferMapping> SharedBufferMapping::Create(
+    base::subtle::PlatformSharedMemoryRegion& region,
+    size_t offset,
+    size_t size) {
+  void* memory;
+  auto mapping = MapPlatformRegion(region, offset, size, &memory);
+  if (!mapping) {
+    return nullptr;
+  }
+
+  return base::MakeRefCounted<SharedBufferMapping>(std::move(mapping), memory);
+}
+
+// static
+scoped_refptr<SharedBufferMapping> SharedBufferMapping::Create(
+    base::subtle::PlatformSharedMemoryRegion& region) {
+  void* memory;
+  auto mapping = MapPlatformRegion(region, 0, region.GetSize(), &memory);
+  if (!mapping) {
+    return nullptr;
+  }
+
+  return base::MakeRefCounted<SharedBufferMapping>(std::move(mapping), memory);
+}
+
+void SharedBufferMapping::Close() {
+  mapping_.reset();
+  memory_ = nullptr;
+}
+
+}  // namespace mojo::core::ipcz_driver
diff --git a/mojo/core/ipcz_driver/shared_buffer_mapping.h b/mojo/core/ipcz_driver/shared_buffer_mapping.h
new file mode 100644
index 0000000..c532d22
--- /dev/null
+++ b/mojo/core/ipcz_driver/shared_buffer_mapping.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_CORE_IPCZ_DRIVER_SHARED_BUFFER_MAPPING_H_
+#define MOJO_CORE_IPCZ_DRIVER_SHARED_BUFFER_MAPPING_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "base/containers/span.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "mojo/core/ipcz_driver/object.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+
+namespace mojo::core::ipcz_driver {
+
+// An active memory mapping of a driver-controlled shared buffer. Note that this
+// is only used to manage read/writable mappings of unsafe regions by ipcz
+// internals.
+class SharedBufferMapping : public Object<SharedBufferMapping> {
+ public:
+  SharedBufferMapping(std::unique_ptr<base::SharedMemoryMapping> mapping,
+                      void* memory);
+
+  static constexpr Type object_type() { return kSharedBufferMapping; }
+
+  void* memory() const { return memory_; }
+
+  static scoped_refptr<SharedBufferMapping> Create(
+      base::subtle::PlatformSharedMemoryRegion& region,
+      size_t offset,
+      size_t size);
+
+  // Maps the whole region.
+  static scoped_refptr<SharedBufferMapping> Create(
+      base::subtle::PlatformSharedMemoryRegion& region);
+
+  // Object:
+  void Close() override;
+
+ private:
+  ~SharedBufferMapping() override;
+
+  std::unique_ptr<base::SharedMemoryMapping> mapping_;
+  void* memory_;
+};
+
+}  // namespace mojo::core::ipcz_driver
+
+#endif  // MOJO_CORE_IPCZ_DRIVER_SHARED_BUFFER_MAPPING_H_
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc
index 2e8e473..831f9a8 100644
--- a/mojo/core/ipcz_driver/transport.cc
+++ b/mojo/core/ipcz_driver/transport.cc
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "mojo/core/core.h"
 #include "mojo/core/ipcz_driver/object.h"
+#include "mojo/core/ipcz_driver/shared_buffer.h"
 #include "mojo/core/ipcz_driver/transmissible_platform_handle.h"
 #include "mojo/core/ipcz_driver/wrapped_platform_handle.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
@@ -341,9 +342,8 @@
       break;
 
     case ObjectBase::kSharedBuffer:
-      // TODO: Implement this.
-      NOTIMPLEMENTED();
-      return IPCZ_RESULT_UNIMPLEMENTED;
+      object = SharedBuffer::Deserialize(object_data, object_handles);
+      break;
 
     case ObjectBase::kTransmissiblePlatformHandle:
       object =
diff --git a/mojo/core/ipcz_driver/transport_test.cc b/mojo/core/ipcz_driver/transport_test.cc
index 71fc5f3..3d76ef17 100644
--- a/mojo/core/ipcz_driver/transport_test.cc
+++ b/mojo/core/ipcz_driver/transport_test.cc
@@ -21,6 +21,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
 #include "mojo/core/ipcz_driver/driver.h"
+#include "mojo/core/ipcz_driver/shared_buffer.h"
 #include "mojo/core/ipcz_driver/transmissible_platform_handle.h"
 #include "mojo/core/ipcz_driver/wrapped_platform_handle.h"
 #include "mojo/core/test/mojo_test_base.h"
@@ -130,6 +131,20 @@
     return base::File(wrapper->TakeHandle().TakeFD());
 #endif
   }
+
+  static TestMessage SerializeRegionFor(Transport& transmitter,
+                                        base::UnsafeSharedMemoryRegion region) {
+    auto handle = base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+        std::move(region));
+    return SerializeObjectFor(
+        transmitter, base::MakeRefCounted<SharedBuffer>(std::move(handle)));
+  }
+
+  base::UnsafeSharedMemoryRegion BufferObjectToRegion(
+      scoped_refptr<SharedBuffer> buffer) {
+    return base::UnsafeSharedMemoryRegion::Deserialize(
+        std::move(buffer->region()));
+  }
 };
 
 // TransportListener provides a convenient way for tests to listen to incoming
@@ -390,5 +405,38 @@
   });
 }
 
+constexpr std::string_view kMemoryMessage = "mojo wuz here";
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(TransmitMemoryClient,
+                                  MojoIpczTransportTest,
+                                  h) {
+  scoped_refptr<Transport> transport = ReceiveTransport(h);
+  TransportListener listener(*transport);
+  const TestMessage message = listener.WaitForNextMessage();
+  auto region = base::UnsafeSharedMemoryRegion::Deserialize(std::move(
+      DeserializeObjectFrom<SharedBuffer>(*transport, message)->region()));
+  EXPECT_EQ(kMemoryMessage.size(), region.GetSize());
+  auto mapping = region.Map();
+  auto contents = std::string_view(static_cast<const char*>(mapping.memory()),
+                                   kMemoryMessage.size());
+  EXPECT_EQ(kMemoryMessage, contents);
+}
+
+TEST_F(MojoIpczTransportTest, TransmitMemory) {
+  RunTestClientWithController("TransmitMemoryClient", [&](ClientController& c) {
+    scoped_refptr<Transport> transport =
+        CreateAndSendTransport(c.pipe(), c.process());
+
+    auto region = base::UnsafeSharedMemoryRegion::Create(kMemoryMessage.size());
+    auto mapping = region.Map();
+    memcpy(mapping.memory(), kMemoryMessage.data(), kMemoryMessage.size());
+    auto buffer = SharedBuffer::MakeForRegion(std::move(region));
+
+    TransportListener listener(*transport);
+    SerializeObjectFor(*transport, std::move(buffer)).Transmit(*transport);
+    listener.WaitForDisconnect();
+  });
+}
+
 }  // namespace
 }  // namespace mojo::core::ipcz_driver
diff --git a/mojo/core/mojo_core.cc b/mojo/core/mojo_core.cc
index 9768872..f074881a 100644
--- a/mojo/core/mojo_core.cc
+++ b/mojo/core/mojo_core.cc
@@ -12,6 +12,7 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/metrics/field_trial.h"
 #include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "base/synchronization/waitable_event.h"
@@ -109,6 +110,16 @@
 #endif
 
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    // If FieldTrialList::GetInstance() returns nullptr,
+    // FeatureList::InitializeFromCommandLine(), used by FeatureList::
+    // InitializeInstance internally, creates no field trials. This causes
+    // DCHECK() failure if we have an command line like
+    // --enable-features=TestFeature:TestParam/TestValue.
+    // We don't need to care about FieldTrialList duplication here, because
+    // this code is available for static build. If base library is not shared,
+    // libmojo_core.so and the caller of LoadAndInitializeCoreLibrary doesn't
+    // share FieldTrialList::GetInstance().
+    field_trial_list_ = std::make_unique<base::FieldTrialList>(nullptr);
     base::FeatureList::InitializeInstance(
         command_line->GetSwitchValueASCII(switches::kEnableFeatures),
         command_line->GetSwitchValueASCII(switches::kDisableFeatures));
@@ -118,6 +129,7 @@
 
  private:
   bool initialized_ = false;
+  std::unique_ptr<base::FieldTrialList> field_trial_list_;
 };
 
 }  // namespace
diff --git a/mojo/core/run_all_core_unittests.cc b/mojo/core/run_all_core_unittests.cc
index f23792c..3334e55 100644
--- a/mojo/core/run_all_core_unittests.cc
+++ b/mojo/core/run_all_core_unittests.cc
@@ -41,7 +41,6 @@
 
 int main(int argc, char** argv) {
   base::TestSuite test_suite(argc, argv);
-  base::test::ScopedFeatureList feature_list;
 
   MojoInitializeFlags flags = MOJO_INITIALIZE_FLAG_NONE;
   const base::CommandLine& command_line =
@@ -53,10 +52,6 @@
   if (command_line.HasSwitch(switches::kMojoUseExplicitLibraryPath))
     library_path = GetMojoCoreLibraryPath();
 
-  feature_list.InitFromCommandLine(
-      command_line.GetSwitchValueASCII(switches::kEnableFeatures),
-      command_line.GetSwitchValueASCII(switches::kDisableFeatures));
-
   if (command_line.HasSwitch(switches::kMojoLoadBeforeInit)) {
     CHECK_EQ(MOJO_RESULT_OK, mojo::LoadCoreLibrary(library_path));
     CHECK_EQ(MOJO_RESULT_OK, mojo::InitializeCoreLibrary(flags));
diff --git a/net/android/javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java b/net/android/javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java
index 3b788d41..8ef7430 100644
--- a/net/android/javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java
+++ b/net/android/javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java
@@ -27,7 +27,7 @@
     /**
      * Called from native code to create a PrivateKey object from its
      * encoded PKCS#8 representation.
-     * @param type The key type, accoding to PrivateKeyType.
+     * @param type The key type, according to PrivateKeyType.
      * @return new PrivateKey handle, or null in case of error.
      */
     @CalledByNative
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index afe1bc8..1cbcbeb 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -493,7 +493,7 @@
   // IMPORTANT: If the path was invalid for a reason that was not
   // explicity checked above, set a general error. This is important as
   // |cert_status| is what ultimately indicates whether verification was
-  // successful or not (absense of errors implies success).
+  // successful or not (absence of errors implies success).
   if (!IsCertStatusError(*cert_status))
     *cert_status |= CERT_STATUS_INVALID;
 }
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 180daca..8535a2c 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -1642,7 +1642,7 @@
     case ERR_QUIC_PROTOCOL_ERROR:
       if (GetResponseHeaders() != nullptr ||
           !stream_->GetAlternativeService(&retried_alternative_service_)) {
-        // If the response headers have already been recieved and passed up
+        // If the response headers have already been received and passed up
         // then the request can not be retried. Also, if there was no
         // alternative service used for this request, then there is no
         // alternative service to be disabled.
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 1db07ee..656a8ef6 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -1389,7 +1389,7 @@
 bool CanGetTaggedBytes();
 
 // Query the system to find out how many bytes were received with tag
-// |expected_tag| for our UID.  Return the count of recieved bytes.
+// |expected_tag| for our UID.  Return the count of received bytes.
 uint64_t GetTaggedBytes(int32_t expected_tag);
 #endif
 
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 532ce52..027dbb6 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -731,7 +731,7 @@
   void SetRequestHeadersCallback(RequestHeadersCallback callback);
 
   // Sets a callback that will be invoked each time the response is received
-  // from the remote party with the actual response headers recieved. Note this
+  // from the remote party with the actual response headers received. Note this
   // is different from response_headers() getter in that in case of revalidation
   // request, the latter will return cached headers, while the callback will be
   // called with a response from the server.
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 0f55a00b..d3218f593 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -242,7 +242,7 @@
   virtual void SetRequestHeadersCallback(RequestHeadersCallback callback) {}
 
   // Sets a callback that will be invoked each time the response is received
-  // from the remote party with the actual response headers recieved.
+  // from the remote party with the actual response headers received.
   virtual void SetResponseHeadersCallback(ResponseHeadersCallback callback) {}
 
   // Sets a callback that will be invoked each time a 103 Early Hints response
diff --git a/sandbox/policy/BUILD.gn b/sandbox/policy/BUILD.gn
index 02088ff..e2a83f5 100644
--- a/sandbox/policy/BUILD.gn
+++ b/sandbox/policy/BUILD.gn
@@ -157,6 +157,7 @@
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.fonts",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hwinfo",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.kernel",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger",
 
       # TODO(crbug.com/1224707): Remove after switching to fuchsia.scheduler API.
diff --git a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
index 3f5cbb9..a0abd1f 100644
--- a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
+++ b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
@@ -14,6 +14,7 @@
 #include <fuchsia/fonts/cpp/fidl.h>
 #include <fuchsia/hwinfo/cpp/fidl.h>
 #include <fuchsia/intl/cpp/fidl.h>
+#include <fuchsia/kernel/cpp/fidl.h>
 #include <fuchsia/logger/cpp/fidl.h>
 #include <fuchsia/media/cpp/fidl.h>
 #include <fuchsia/memorypressure/cpp/fidl.h>
@@ -122,6 +123,7 @@
 constexpr SandboxConfig kRendererConfig = {
     base::make_span((const char* const[]){
         fuchsia::fonts::Provider::Name_,
+        fuchsia::kernel::VmexResource::Name_,
         // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead.
         fuchsia::media::ProfileProvider::Name_,
         fuchsia::memorypressure::Provider::Name_,
@@ -140,7 +142,8 @@
 };
 
 constexpr SandboxConfig kServiceWithJitConfig = {
-    base::span<const char* const>(),
+    base::make_span(
+        (const char* const[]){fuchsia::kernel::VmexResource::Name_}),
     kAmbientMarkVmoAsExecutable,
 };
 
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
index 9034c462..fa542c6 100644
--- a/sandbox/win/src/sandbox_policy_base.cc
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -196,11 +196,10 @@
       // Win32k intercept rules only supported on Windows 8 and above. This must
       // match the version checks in process_mitigations.cc for consistency.
       if (base::win::GetVersion() >= base::win::Version::WIN8) {
-        // TODO(549319) Re-enable dcheck once mitigations move to TargetConfig.
-        // DCHECK_EQ(MITIGATION_WIN32K_DISABLE,
-        //          mitigations_ & MITIGATION_WIN32K_DISABLE)
-        //    << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
-        //       "rules.";
+        DCHECK_EQ(MITIGATION_WIN32K_DISABLE,
+                  mitigations_ & MITIGATION_WIN32K_DISABLE)
+            << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
+               "rules.";
         if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
                 pattern, semantics, policy_maker_.get())) {
           NOTREACHED();
@@ -214,11 +213,10 @@
       // must match the version checks in process_mitigations.cc for
       // consistency.
       if (base::win::GetVersion() >= base::win::Version::WIN10_TH2) {
-        // TODO(549319) Re-enable dcheck once mitigations move to TargetConfig.
-        // DCHECK_EQ(MITIGATION_FORCE_MS_SIGNED_BINS,
-        //          mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS)
-        //    << "Enable MITIGATION_FORCE_MS_SIGNED_BINS before adding signed "
-        //       "policy rules.";
+        DCHECK_EQ(MITIGATION_FORCE_MS_SIGNED_BINS,
+                  mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS)
+            << "Enable MITIGATION_FORCE_MS_SIGNED_BINS before adding signed "
+               "policy rules.";
         if (!SignedPolicy::GenerateRules(pattern, semantics,
                                          policy_maker_.get())) {
           NOTREACHED();
diff --git a/services/BUILD.gn b/services/BUILD.gn
index b922497c..20c258d 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -89,8 +89,10 @@
 
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
 
-    additional_manifest_fragments =
-        [ "//build/config/fuchsia/test/network.shard.test-cml" ]
+    additional_manifest_fragments = [
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
+      "//build/config/fuchsia/test/network.shard.test-cml",
+    ]
   }
 }
 
diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc
index 7e02d8c9..3d34279 100644
--- a/services/network/public/cpp/resource_request.cc
+++ b/services/network/public/cpp/resource_request.cc
@@ -244,8 +244,6 @@
              request.custom_proxy_post_cache_headers.ToString() &&
          fetch_window_id == request.fetch_window_id &&
          devtools_request_id == request.devtools_request_id &&
-         is_signed_exchange_prefetch_cache_enabled ==
-             request.is_signed_exchange_prefetch_cache_enabled &&
          is_fetch_like_api == request.is_fetch_like_api &&
          is_favicon == request.is_favicon &&
          recursive_prefetch_token == request.recursive_prefetch_token &&
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index 10d79120..6623b44 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -168,7 +168,6 @@
   absl::optional<base::UnguessableToken> fetch_window_id;
   absl::optional<std::string> devtools_request_id;
   absl::optional<std::string> devtools_stack_id;
-  bool is_signed_exchange_prefetch_cache_enabled = false;
   bool is_fetch_like_api = false;
   bool is_favicon = false;
   absl::optional<base::UnguessableToken> recursive_prefetch_token;
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index 10295a16..545e9c1 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -212,8 +212,6 @@
   out->previews_state = data.previews_state();
   out->upgrade_if_insecure = data.upgrade_if_insecure();
   out->is_revalidating = data.is_revalidating();
-  out->is_signed_exchange_prefetch_cache_enabled =
-      data.is_signed_exchange_prefetch_cache_enabled();
   out->is_fetch_like_api = data.is_fetch_like_api();
   out->is_favicon = data.is_favicon();
   out->original_destination = data.original_destination();
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index b4f7855..1c69fdc 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -302,10 +302,6 @@
       const network::ResourceRequest& request) {
     return request.devtools_stack_id;
   }
-  static bool is_signed_exchange_prefetch_cache_enabled(
-      const network::ResourceRequest& request) {
-    return request.is_signed_exchange_prefetch_cache_enabled;
-  }
   static bool is_fetch_like_api(const network::ResourceRequest& request) {
     return request.is_fetch_like_api;
   }
diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom
index 05cc03e8..bc2d669 100644
--- a/services/network/public/mojom/url_request.mojom
+++ b/services/network/public/mojom/url_request.mojom
@@ -361,12 +361,6 @@
   // handler.
   string? devtools_stack_id;
 
-  // True for prefetch requests when SignedExchangePrefetchCacheForNavigations
-  // feature is enabled by flags or SignedExchangeSubresourcePrefetch feature is
-  // enabled by flags, or OriginTrial. TODO(horo): Remove this when these
-  // features are enabled by default.
-  bool is_signed_exchange_prefetch_cache_enabled;
-
   // True for XHR, Fetch, and EventSource.
   bool is_fetch_like_api;
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index cebfd4f..41fd25f2 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -932,6 +932,42 @@
             ]
         }
     ],
+    "AutofillAssistantNiuCowinVaccinationInChromeTriggering": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "json_parameters": "\n        {\n          \"intent\":\"NIU_COWIN_CERTIFICATE_DOWNLOAD\",\n          \"heuristics\":[\n           {\n             \"conditionSet\":{\"\n               urlMatches\":\"https://www.cowin.gov.in\"\n              }\n            }\n           ],\n          \"enabledInCustomTabs\":true,\n          \"enabledForSignedOutUsers\":true\n        }\n        "
+                    },
+                    "enable_features": [
+                        "AutofillAssistantUrlHeuristic1"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AutofillAssistantNiuWtvInChromeTriggering": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "json_parameters": "\n        {\n          \"intent\":\"NIU_BRAZIL_WHERE_TO_VOTE\",\n          \"heuristics\":[\n           {\n            \"conditionSet\":{\n              \"urlMatches\":\"https://www.tse.jus.br\"\n             }\n           }\n          ],\n          \"enabledInCustomTabs\":true,\n          \"enabledForSignedOutUsers\":true\n        }\n        "
+                    },
+                    "enable_features": [
+                        "AutofillAssistantUrlHeuristic2"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillConsiderVariationCountryCodeForPhoneNumbers": [
         {
             "platforms": [
@@ -2167,6 +2203,21 @@
             ]
         }
     ],
+    "BiometricAuthenticationInSettingsStudy": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "BiometricAuthenticationInSettings"
+                    ]
+                }
+            ]
+        }
+    ],
     "BlinkSchedulerPrioritizeRenderingAfterInput": [
         {
             "platforms": [
@@ -3139,6 +3190,21 @@
             ]
         }
     ],
+    "ContextualSearchSuppressShortView": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ContextualSearchSuppressShortView"
+                    ]
+                }
+            ]
+        }
+    ],
     "ContextualTriggers": [
         {
             "platforms": [
@@ -6704,7 +6770,10 @@
             ],
             "experiments": [
                 {
-                    "name": "EnabledPrerenderDUIAndDSE_20220419",
+                    "name": "EnabledPrerenderDUIAndDSE_20220802",
+                    "params": {
+                        "implementation_type": "use_prefetch"
+                    },
                     "enable_features": [
                         "OmniboxTriggerForPrerender2",
                         "SupportSearchSuggestionForPrerender2"
@@ -6714,7 +6783,7 @@
                     ]
                 },
                 {
-                    "name": "EnabledPrerenderDUI_20220419",
+                    "name": "EnabledPrerenderDUI_20220802",
                     "enable_features": [
                         "OmniboxTriggerForPrerender2"
                     ],
@@ -6724,7 +6793,7 @@
                     ]
                 },
                 {
-                    "name": "EnabledPrefetchDUIControl_20220419",
+                    "name": "EnabledPrefetchDUIControl_20220802",
                     "enable_features": [
                         "OmniboxTriggerForNoStatePrefetch"
                     ],
@@ -6734,7 +6803,7 @@
                     ]
                 },
                 {
-                    "name": "DisabledDUI_20220419",
+                    "name": "DisabledDUI_20220802",
                     "disable_features": [
                         "OmniboxTriggerForNoStatePrefetch",
                         "OmniboxTriggerForPrerender2",
@@ -6742,7 +6811,10 @@
                     ]
                 },
                 {
-                    "name": "EnabledPrerenderDSE_20220419",
+                    "name": "EnabledPrerenderDSE_20220802",
+                    "params": {
+                        "implementation_type": "use_prefetch"
+                    },
                     "enable_features": [
                         "SupportSearchSuggestionForPrerender2"
                     ],
@@ -9134,6 +9206,22 @@
             ]
         }
     ],
+    "StylusWritingToInput": [
+        {
+            "platforms": [
+                "android",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "StylusWritingToInput"
+                    ]
+                }
+            ]
+        }
+    ],
     "SubframeShutdownDelay": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index e0ad9c8..02aa250c 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -390,14 +390,6 @@
 const base::Feature kWindowOpenNewPopupBehavior{
     "WindowOpenNewPopupBehavior", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Changes the default RTCPeerConnection constructor behavior to use Unified
-// Plan as the SDP semantics. When the feature is enabled, Unified Plan is used
-// unless the default is overridden (by passing {sdpSemantics:'plan-b'} as the
-// argument). This was shipped in M72.
-// The feature is still used by virtual test suites exercising Plan B.
-const base::Feature kRTCUnifiedPlanByDefault{"RTCUnifiedPlanByDefault",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Determines if the SDP attrbute extmap-allow-mixed should be offered by
 // default or not. The default value can be overridden by passing
 // {offerExtmapAllowMixed:false} as an argument to the RTCPeerConnection
@@ -1607,6 +1599,12 @@
 const base::Feature kClipboardUnsanitizedContent{
     "ClipboardUnsanitizedContent", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kThreadedHtmlTokenizer{"ThreadedHtmlTokenizer",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<int> kThreadedHtmlTokenizerTokenMaxCount{
+    &kThreadedHtmlTokenizer, "max-count", 2048};
+
 const base::Feature kWebRtcThreadsUseResourceEfficientType{
     "WebRtcThreadsUseResourceEfficientType", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 3b838d1..2c14e34a 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -187,7 +187,6 @@
 BLINK_COMMON_EXPORT extern const base::Feature
     kPurgeRendererMemoryWhenBackgrounded;
 BLINK_COMMON_EXPORT extern const base::Feature kWindowOpenNewPopupBehavior;
-BLINK_COMMON_EXPORT extern const base::Feature kRTCUnifiedPlanByDefault;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCOfferExtmapAllowMixed;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCGpuCodecSupportWaiter;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
@@ -857,6 +856,14 @@
 // it is only applicable to HTML format. See crbug.com/1268679.
 BLINK_COMMON_EXPORT extern const base::Feature kClipboardUnsanitizedContent;
 
+// If set, HTMLTokenizer is run on a background thread.
+BLINK_COMMON_EXPORT extern const base::Feature kThreadedHtmlTokenizer;
+
+// The maximum number of tokens the background thread will generate before
+// NextParseResults() is called.
+BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
+    kThreadedHtmlTokenizerTokenMaxCount;
+
 // If enabled, the WebRTC_* threads in peerconnection module will use
 // kResourceEfficient thread type.
 BLINK_COMMON_EXPORT extern const base::Feature
diff --git a/third_party/blink/public/mojom/navigation/navigation_params.mojom b/third_party/blink/public/mojom/navigation/navigation_params.mojom
index 7161e68d..1970472e 100644
--- a/third_party/blink/public/mojom/navigation/navigation_params.mojom
+++ b/third_party/blink/public/mojom/navigation/navigation_params.mojom
@@ -376,8 +376,7 @@
   // same-document browser-initiated navigations are properly handled as well.
   mojo_base.mojom.UnguessableToken navigation_token;
 
-  // Prefetched signed exchanges. Used when SignedExchangeSubresourcePrefetch
-  // feature is enabled.
+  // Prefetched signed exchanges.
   array<blink.mojom.PrefetchedSignedExchangeInfo> prefetched_signed_exchanges;
 
   // The real content of the data: URL. Only used in Android WebView for
diff --git a/third_party/blink/public/mojom/navigation/prefetched_signed_exchange_info.mojom b/third_party/blink/public/mojom/navigation/prefetched_signed_exchange_info.mojom
index 19dd458..8d286d1 100644
--- a/third_party/blink/public/mojom/navigation/prefetched_signed_exchange_info.mojom
+++ b/third_party/blink/public/mojom/navigation/prefetched_signed_exchange_info.mojom
@@ -14,7 +14,6 @@
   mojo_base.mojom.ByteString data;
 };
 
-// Used for SignedExchangeSubresourcePrefetch.
 // This struct keeps the information about a prefetched signed exchange. It is
 // used in CommitNavigationParams to be passed from the browser process to the
 // renderer process in CommitNavigation IPC.
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 5d75265..94ce877 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -1939,7 +1939,7 @@
   kCSSValueAppearanceSquareButtonRendered = 2586,
   kCursorImageLE32x32 = 2589,
   kCursorImageGT32x32 = 2590,
-  kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics = 2591,
+  kOBSOLETE_RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics = 2591,
   // The above items are available in M71 branch.
 
   kResizeObserver_Constructor = 2592,
@@ -3077,7 +3077,7 @@
   kAddressSpaceUnknownNonSecureContextNavigatedToLocal = 3770,
   kAddressSpaceUnknownSecureContextNavigatedToPrivate = 3771,
   kAddressSpaceUnknownNonSecureContextNavigatedToPrivate = 3772,
-  kRTCPeerConnectionSdpSemanticsPlanB = 3773,
+  kOBSOLETE_RTCPeerConnectionSdpSemanticsPlanB = 3773,
   // The items above roughly this point are available in the M89 branch.
 
   kFetchRespondWithNoResponseWithUsedRequestBody = 3774,
@@ -3091,10 +3091,10 @@
   kOBSOLETE_V8HTMLPopupElement_Show_Method = 3782,
   kOBSOLETE_V8HTMLPopupElement_Hide_Method = 3783,
   kOBSOLETE_WindowOpenWithAdditionalBoolParameter = 3784,
-  kRTCPeerConnectionConstructedWithPlanB = 3785,
-  kRTCPeerConnectionConstructedWithUnifiedPlan = 3786,
-  kRTCPeerConnectionUsingComplexPlanB = 3787,
-  kRTCPeerConnectionUsingComplexUnifiedPlan = 3788,
+  kOBSOLETE_RTCPeerConnectionConstructedWithPlanB = 3785,
+  kOBSOLETE_RTCPeerConnectionConstructedWithUnifiedPlan = 3786,
+  kOBSOLETE_RTCPeerConnectionUsingComplexPlanB = 3787,
+  kOBSOLETE_RTCPeerConnectionUsingComplexUnifiedPlan = 3788,
   kWindowScreenIsExtended = 3789,
   kWindowScreenChange = 3790,
   kXRWebGLDepthInformationTextureAttribute = 3791,
@@ -3242,7 +3242,7 @@
   kSpeculationRules = 3932,
   kV8AbortSignal_Abort_Method = 3933,
   kSelectionBackgroundColorInversion = 3934,
-  kRTCPeerConnectionPlanBThrewAnException = 3935,
+  kOBSOLETE_RTCPeerConnectionPlanBThrewAnException = 3935,
   kHTMLRootContained = 3936,
   kHTMLBodyContained = 3937,
   kXRFrameGetJointPose = 3938,
diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h
index 6390e4b8..fd55462 100644
--- a/third_party/blink/public/platform/web_url_request.h
+++ b/third_party/blink/public/platform/web_url_request.h
@@ -302,8 +302,6 @@
 
   BLINK_PLATFORM_EXPORT bool IsFromOriginDirtyStyleSheet() const;
 
-  BLINK_PLATFORM_EXPORT bool IsSignedExchangePrefetchCacheEnabled() const;
-
   BLINK_PLATFORM_EXPORT absl::optional<base::UnguessableToken>
   RecursivePrefetchToken() const;
 
diff --git a/third_party/blink/public/web/web_element.h b/third_party/blink/public/web/web_element.h
index 5d18b3d..ee97a96 100644
--- a/third_party/blink/public/web/web_element.h
+++ b/third_party/blink/public/web/web_element.h
@@ -105,7 +105,7 @@
   // Returns the bounds of the element in Visual Viewport. The bounds
   // have been adjusted to include any transformations, including page scale.
   // This function will update the layout if required.
-  gfx::Rect BoundsInViewport() const;
+  gfx::Rect BoundsInWidget() const;
 
   // Returns the image contents of this element or a null SkBitmap
   // if there isn't any.
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 2167eb46..1c4dcd5 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -356,7 +356,6 @@
   // `RenderFrameImpl::SynchronouslyConmmitAboutBlankForBug778318`.
   bool is_synchronous_commit_for_bug_778318 = false;
 
-  // Used for SignedExchangeSubresourcePrefetch.
   // This struct keeps the information about a prefetched signed exchange.
   struct BLINK_EXPORT PrefetchedSignedExchange {
     PrefetchedSignedExchange();
diff --git a/third_party/blink/renderer/controller/BUILD.gn b/third_party/blink/renderer/controller/BUILD.gn
index 7a0d957..33b0769 100644
--- a/third_party/blink/renderer/controller/BUILD.gn
+++ b/third_party/blink/renderer/controller/BUILD.gn
@@ -156,6 +156,7 @@
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
       "//build/config/fuchsia/test/network.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
     ]
   }
 }
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 90e0466..c07db55 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -13,6 +13,7 @@
     "+base/strings/stringprintf.h",
     "+base/substring_set_matcher/substring_set_matcher.h",
     "+base/task/sequence_manager/task_time_observer.h",
+    "+base/task/task_traits.h",
     "+base/test/bind.h",
     "+base/test/simple_test_tick_clock.h",
     "+base/threading/thread_restrictions.h",
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index cb9865c..3de2fc2 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -6011,6 +6011,93 @@
   EXPECT_TRUE(ConsoleMessages().Contains(CQLegacyWarningText()));
 }
 
+TEST_F(StyleEngineSimTest,
+       ShouldInvalidateSubjectPseudoHasAfterChildrenParsingFinished) {
+  SimRequest main_resource("https://example.com/", "text/html");
+
+  LoadURL("https://example.com/");
+
+  main_resource.Write(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      .a { color: black }
+      .a:not(:has(+ div)) { color: red }
+    </style>
+    <div id="first" class="a"> First </div>
+    <div id="second" class="a"> Second
+  )HTML");
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  Element* first = GetDocument().getElementById("first");
+  EXPECT_TRUE(first);
+  EXPECT_EQ(
+      Color::FromRGB(0, 0, 0),
+      first->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  Element* second = GetDocument().getElementById("second");
+  EXPECT_TRUE(second);
+  EXPECT_EQ(
+      Color::FromRGB(255, 0, 0),
+      second->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  main_resource.Write(R"HTML(
+    </div>
+    <div id="third" class="a"> Third
+  )HTML");
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  first = GetDocument().getElementById("first");
+  EXPECT_TRUE(first);
+  EXPECT_EQ(
+      Color::FromRGB(0, 0, 0),
+      first->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  second = GetDocument().getElementById("second");
+  EXPECT_TRUE(second);
+  EXPECT_EQ(
+      Color::FromRGB(255, 0, 0),
+      second->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  Element* third = GetDocument().getElementById("third");
+  EXPECT_TRUE(third);
+  EXPECT_EQ(
+      Color::FromRGB(255, 0, 0),
+      third->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  main_resource.Complete(R"HTML(
+    </div>
+    <div id="fourth"> Fourth </div>
+  )HTML");
+
+  first = GetDocument().getElementById("first");
+  EXPECT_TRUE(first);
+  EXPECT_EQ(
+      Color::FromRGB(0, 0, 0),
+      first->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  second = GetDocument().getElementById("second");
+  EXPECT_TRUE(second);
+  EXPECT_EQ(
+      Color::FromRGB(0, 0, 0),
+      second->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  third = GetDocument().getElementById("third");
+  EXPECT_TRUE(third);
+  EXPECT_EQ(
+      Color::FromRGB(0, 0, 0),
+      third->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+
+  Element* fourth = GetDocument().getElementById("fourth");
+  EXPECT_TRUE(fourth);
+  EXPECT_EQ(
+      Color::FromRGB(0, 0, 0),
+      fourth->GetComputedStyle()->VisitedDependentColor(GetCSSPropertyColor()));
+}
+
 TEST_F(StyleEngineTest, UsesCachedTokenizer) {
   // Make sure the parser exists.
   GetDocument().write("<body></body>");
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 89bcf662..1e9e25ec 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -5102,10 +5102,10 @@
 
     if (new_focused_element->IsSVGElement()) {
       // Convert to window coordinate system (this will be in DIPs).
-      bounds_in_viewport = new_focused_element->BoundsInViewport();
+      bounds_in_viewport = new_focused_element->BoundsInWidget();
     } else {
       Vector<gfx::Rect> outline_rects =
-          new_focused_element->OutlineRectsInVisualViewport(
+          new_focused_element->OutlineRectsInWidget(
               DocumentUpdateReason::kFocus);
       for (auto& outline_rect : outline_rects)
         bounds_in_viewport.Union(outline_rect);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 361bd8c..6a493595 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -2518,8 +2518,6 @@
 
   bool rendering_has_begun_ = false;
 
-  int async_script_count_ = 0;
-
   DeclarativeShadowRootAllowState declarative_shadow_root_allow_state_ =
       DeclarativeShadowRootAllowState::kNotSet;
 
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index eb349e8..b6ca73b 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1974,7 +1974,7 @@
                             scroll_behavior);
 }
 
-gfx::Rect Element::BoundsInViewport() const {
+gfx::Rect Element::BoundsInWidget() const {
   GetDocument().EnsurePaintLocationDataValidForNode(
       this, DocumentUpdateReason::kUnknown);
 
@@ -2015,7 +2015,7 @@
   return view->FrameToViewport(gfx::ToEnclosingRect(result));
 }
 
-Vector<gfx::Rect> Element::OutlineRectsInVisualViewport(
+Vector<gfx::Rect> Element::OutlineRectsInWidget(
     DocumentUpdateReason reason) const {
   Vector<gfx::Rect> rects;
 
@@ -5406,8 +5406,8 @@
                               nullptr);
   GetDocument()
       .GetStyleEngine()
-      .ScheduleInvalidationsForHasPseudoAffectedByInsertion(parentElement(),
-                                                            lastChild(), *this);
+      .ScheduleInvalidationsForHasPseudoAffectedByInsertion(
+          parentElement(), previousSibling(), *this);
 }
 
 AttrNodeList* Element::GetAttrNodeList() {
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index bb3f00c4..b09c5b6b 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -397,14 +397,23 @@
   void scrollTo(const ScrollToOptions*);
   LayoutBox* GetLayoutBoxForScrolling() const override;
 
-  gfx::Rect BoundsInViewport() const;
+  // Returns the bounds of this Element, unclipped, in the coordinate space of
+  // the local root's widget. That is, in the outermost main frame, this will
+  // scale and transform the bounds by the visual viewport transform (i.e.
+  // pinch-zoom). In a local root that isn't main (i.e. a remote frame), the
+  // returned bounds are unscaled by the visual viewport and are relative to
+  // the local root frame.
+  gfx::Rect BoundsInWidget() const;
+
+  // Same as above but for outline rects.
+  Vector<gfx::Rect> OutlineRectsInWidget(
+      DocumentUpdateReason reason = DocumentUpdateReason::kUnknown) const;
+
   // Returns an intersection rectangle of the bounds rectangle and the visual
   // viewport's rectangle in the visual viewport's coordinate space.
   // Applies ancestors' frames' clipping, but does not (yet) apply (overflow)
   // element clipping (crbug.com/889840).
   gfx::Rect VisibleBoundsInVisualViewport() const;
-  Vector<gfx::Rect> OutlineRectsInVisualViewport(
-      DocumentUpdateReason reason = DocumentUpdateReason::kUnknown) const;
 
   DOMRectList* getClientRects();
   gfx::RectF GetBoundingClientRectNoLifecycleUpdate() const;
diff --git a/third_party/blink/renderer/core/dom/element_test.cc b/third_party/blink/renderer/core/dom/element_test.cc
index 3ec9541..b4241327b 100644
--- a/third_party/blink/renderer/core/dom/element_test.cc
+++ b/third_party/blink/renderer/core/dom/element_test.cc
@@ -131,7 +131,7 @@
   EXPECT_EQ(DocumentLifecycle::kLayoutClean, document.Lifecycle().GetState());
 }
 
-TEST_F(ElementTest, BoundsInViewportCorrectForStickyElementsAfterInsertion) {
+TEST_F(ElementTest, BoundsInWidgetCorrectForStickyElementsAfterInsertion) {
   Document& document = GetDocument();
   SetBodyContent(R"HTML(
     <style>body { margin: 0 }
@@ -154,7 +154,7 @@
 
   // The sticky element should remain at (0, 25) relative to the viewport due to
   // the constraints.
-  gfx::Rect bounds_in_viewport = sticky->BoundsInViewport();
+  gfx::Rect bounds_in_viewport = sticky->BoundsInWidget();
   EXPECT_EQ(0, bounds_in_viewport.y());
   EXPECT_EQ(25, bounds_in_viewport.x());
 
@@ -166,7 +166,7 @@
 
   // Requesting the bounds in viewport should cause both layout and compositing
   // inputs clean to be run, and the sticky result shouldn't change.
-  bounds_in_viewport = sticky->BoundsInViewport();
+  bounds_in_viewport = sticky->BoundsInWidget();
   EXPECT_EQ(DocumentLifecycle::kLayoutClean, document.Lifecycle().GetState());
   EXPECT_EQ(0, bounds_in_viewport.y());
   EXPECT_EQ(25, bounds_in_viewport.x());
@@ -185,11 +185,11 @@
   ASSERT_TRUE(img);
 
   // The a element should include the image in computing its bounds.
-  gfx::Rect img_bounds_in_viewport = img->BoundsInViewport();
+  gfx::Rect img_bounds_in_viewport = img->BoundsInWidget();
   EXPECT_EQ(220, img_bounds_in_viewport.width());
   EXPECT_EQ(147, img_bounds_in_viewport.height());
 
-  Vector<gfx::Rect> a_outline_rects = a->OutlineRectsInVisualViewport();
+  Vector<gfx::Rect> a_outline_rects = a->OutlineRectsInWidget();
   EXPECT_EQ(2u, a_outline_rects.size());
 
   gfx::Rect a_outline_rect;
@@ -307,7 +307,7 @@
   EXPECT_EQ(100, rect_bounding_client_rect->top());
   EXPECT_EQ(100, rect_bounding_client_rect->width());
   EXPECT_EQ(71, rect_bounding_client_rect->height());
-  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), rect->BoundsInViewport());
+  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), rect->BoundsInWidget());
 
   // TODO(pdr): Should we should be excluding the stroke (here, and below)?
   // See: https://github.com/w3c/svgwg/issues/339 and Element::ClientQuads.
@@ -317,8 +317,9 @@
   EXPECT_EQ(100, stroke_bounding_client_rect->top());
   EXPECT_EQ(100, stroke_bounding_client_rect->width());
   EXPECT_EQ(71, stroke_bounding_client_rect->height());
-  // TODO(pdr): BoundsInViewport is not web exposed and should include stroke.
-  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), stroke->BoundsInViewport());
+  // TODO(pdr): BoundsInWidget is not web exposed and should include
+  // stroke.
+  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), stroke->BoundsInWidget());
 
   Element* stroke_transformed = document.getElementById("stroke_transformed");
   DOMRect* stroke_transformedbounding_client_rect =
@@ -327,9 +328,9 @@
   EXPECT_EQ(105, stroke_transformedbounding_client_rect->top());
   EXPECT_EQ(100, stroke_transformedbounding_client_rect->width());
   EXPECT_EQ(71, stroke_transformedbounding_client_rect->height());
-  // TODO(pdr): BoundsInViewport is not web exposed and should include stroke.
-  EXPECT_EQ(gfx::Rect(13, 105, 100, 71),
-            stroke_transformed->BoundsInViewport());
+  // TODO(pdr): BoundsInWidget is not web exposed and should include
+  // stroke.
+  EXPECT_EQ(gfx::Rect(13, 105, 100, 71), stroke_transformed->BoundsInWidget());
 
   Element* foreign = document.getElementById("foreign");
   DOMRect* foreign_bounding_client_rect = foreign->getBoundingClientRect();
@@ -337,7 +338,7 @@
   EXPECT_EQ(100, foreign_bounding_client_rect->top());
   EXPECT_EQ(100, foreign_bounding_client_rect->width());
   EXPECT_EQ(71, foreign_bounding_client_rect->height());
-  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), foreign->BoundsInViewport());
+  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), foreign->BoundsInWidget());
 
   Element* foreign_transformed = document.getElementById("foreign_transformed");
   DOMRect* foreign_transformed_bounding_client_rect =
@@ -346,8 +347,7 @@
   EXPECT_EQ(105, foreign_transformed_bounding_client_rect->top());
   EXPECT_EQ(100, foreign_transformed_bounding_client_rect->width());
   EXPECT_EQ(71, foreign_transformed_bounding_client_rect->height());
-  EXPECT_EQ(gfx::Rect(13, 105, 100, 71),
-            foreign_transformed->BoundsInViewport());
+  EXPECT_EQ(gfx::Rect(13, 105, 100, 71), foreign_transformed->BoundsInWidget());
 
   Element* svg = document.getElementById("svg");
   DOMRect* svg_bounding_client_rect = svg->getBoundingClientRect();
@@ -355,7 +355,7 @@
   EXPECT_EQ(100, svg_bounding_client_rect->top());
   EXPECT_EQ(100, svg_bounding_client_rect->width());
   EXPECT_EQ(71, svg_bounding_client_rect->height());
-  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), svg->BoundsInViewport());
+  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), svg->BoundsInWidget());
 
   Element* svg_stroke = document.getElementById("svg_stroke");
   DOMRect* svg_stroke_bounding_client_rect =
@@ -364,8 +364,9 @@
   EXPECT_EQ(100, svg_stroke_bounding_client_rect->top());
   EXPECT_EQ(100, svg_stroke_bounding_client_rect->width());
   EXPECT_EQ(71, svg_stroke_bounding_client_rect->height());
-  // TODO(pdr): BoundsInViewport is not web exposed and should include stroke.
-  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), svg_stroke->BoundsInViewport());
+  // TODO(pdr): BoundsInWidget is not web exposed and should include
+  // stroke.
+  EXPECT_EQ(gfx::Rect(10, 100, 100, 71), svg_stroke->BoundsInWidget());
 }
 
 TEST_F(ElementTest, PartAttribute) {
diff --git a/third_party/blink/renderer/core/editing/frame_selection_test.cc b/third_party/blink/renderer/core/editing/frame_selection_test.cc
index f642a16..140622c 100644
--- a/third_party/blink/renderer/core/editing/frame_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection_test.cc
@@ -937,7 +937,7 @@
   // We use a double click to create the selection [Berlin].
   // FrameSelection::SelectAll (= textarea.select() in JavaScript) would have
   // been shorter, but currently that doesn't work on a *disabled* text control.
-  const gfx::Rect elem_bounds = textarea->BoundsInViewport();
+  const gfx::Rect elem_bounds = textarea->BoundsInWidget();
   WebMouseEvent double_click(WebMouseEvent::Type::kMouseDown, 0,
                              WebInputEvent::GetStaticTimeStampForTests());
   double_click.SetPositionInWidget(elem_bounds.x(), elem_bounds.y());
diff --git a/third_party/blink/renderer/core/editing/ime/input_method_controller.cc b/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
index 9fc1f5a5..e05d7f5 100644
--- a/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
+++ b/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
@@ -1541,7 +1541,7 @@
   // Selection bounds are currently populated only for EditContext.
   // For editable elements we use GetCompositionCharacterBounds to fetch the
   // selection bounds.
-  *control_bounds = element->BoundsInViewport();
+  *control_bounds = element->BoundsInWidget();
 }
 
 void InputMethodController::DidChangeVisibility(
diff --git a/third_party/blink/renderer/core/editing/link_selection_test.cc b/third_party/blink/renderer/core/editing/link_selection_test.cc
index 40a5e13..eb1ce30 100644
--- a/third_party/blink/renderer/core/editing/link_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/link_selection_test.cc
@@ -315,7 +315,7 @@
     testing::InSequence s;
     EXPECT_CALL(*event_handler, Invoke(_, _)).Times(1);
 
-    const auto& elem_bounds = element.BoundsInViewport();
+    const auto& elem_bounds = element.BoundsInWidget();
     const int click_count = double_click_event ? 2 : 1;
     EmulateMouseClick(elem_bounds.CenterPoint(), WebMouseEvent::Button::kLeft,
                       0, click_count);
diff --git a/third_party/blink/renderer/core/exported/web_element.cc b/third_party/blink/renderer/core/exported/web_element.cc
index 108cd3e..4289ba9 100644
--- a/third_party/blink/renderer/core/exported/web_element.cc
+++ b/third_party/blink/renderer/core/exported/web_element.cc
@@ -179,8 +179,8 @@
   return WebNode(root);
 }
 
-gfx::Rect WebElement::BoundsInViewport() const {
-  return ConstUnwrap<Element>()->BoundsInViewport();
+gfx::Rect WebElement::BoundsInWidget() const {
+  return ConstUnwrap<Element>()->BoundsInWidget();
 }
 
 SkBitmap WebElement::ImageContents() {
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index 3f2df7a..4c517909 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -728,7 +728,7 @@
 
   // Next, send an event that does hit the plugin, and verify it does receive
   // it.
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(
       gfx::PointF(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2));
 
@@ -762,7 +762,7 @@
       gfx::Point(30, 30),
       WebInputEvent::kMiddleButtonDown | WebInputEvent::kShiftKey);
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(rect.x() + rect.width() / 2,
                             rect.y() + rect.height() / 2);
 
@@ -797,7 +797,7 @@
                            WebInputEvent::kNoModifiers,
                            WebInputEvent::GetStaticTimeStampForTests());
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(rect.x() + rect.width() / 2,
                             rect.y() + rect.height() / 2);
 
@@ -833,7 +833,7 @@
                           ->Plugin();
   EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   WebPointerEvent event(
       WebInputEvent::Type::kPointerDown,
       WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
@@ -878,7 +878,7 @@
   EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
 
   {
-    gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+    gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
     WebPointerEvent event(
         WebInputEvent::Type::kPointerDown,
         WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
@@ -904,7 +904,7 @@
   }
 
   {
-    gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+    gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
     WebPointerEvent event1(
         WebInputEvent::Type::kPointerMove,
         WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
@@ -978,7 +978,7 @@
                            WebInputEvent::kNoModifiers,
                            WebInputEvent::GetStaticTimeStampForTests());
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(rect.x() + rect.width() / 2,
                             rect.y() + rect.height() / 2);
 
@@ -1018,7 +1018,7 @@
                       WebInputEvent::kNoModifiers,
                       WebInputEvent::GetStaticTimeStampForTests());
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(rect.x() + rect.width() / 2,
                             rect.y() + rect.height() / 2);
 
@@ -1061,7 +1061,7 @@
                       WebInputEvent::kNoModifiers,
                       WebInputEvent::GetStaticTimeStampForTests());
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(rect.x() + rect.width() / 2,
                             rect.y() + rect.height() / 2);
 
@@ -1106,7 +1106,7 @@
                            WebInputEvent::kNoModifiers,
                            WebInputEvent::GetStaticTimeStampForTests());
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   event.SetPositionInWidget(rect.x() + rect.width() / 2,
                             rect.y() + rect.height() / 2);
 
@@ -1147,7 +1147,7 @@
                           ->Plugin();
   EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
 
-  gfx::Rect rect = plugin_container_one_element.BoundsInViewport();
+  gfx::Rect rect = plugin_container_one_element.BoundsInWidget();
   WebPointerEvent event(
       WebInputEvent::Type::kPointerDown,
       WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
@@ -1186,7 +1186,7 @@
           web_view, WebString::FromUTF8("translated-plugin")));
   plugin_container_impl->SetFrameRect(gfx::Rect(0, 0, 300, 300));
 
-  gfx::Rect rect = plugin_container_impl->GetElement().BoundsInViewport();
+  gfx::Rect rect = plugin_container_impl->GetElement().BoundsInWidget();
   EXPECT_TRUE(plugin_container_impl->IsRectTopmost(rect));
 
   // Cause the plugin's frame to be detached.
@@ -1209,14 +1209,14 @@
       To<WebPluginContainerImpl>(GetWebPluginContainer(
           web_view, WebString::FromUTF8("translated-plugin")));
   even_plugin_container_impl->SetFrameRect(gfx::Rect(0, 0, 300, 300));
-  auto even_rect = even_plugin_container_impl->GetElement().BoundsInViewport();
+  auto even_rect = even_plugin_container_impl->GetElement().BoundsInWidget();
   EXPECT_TRUE(even_plugin_container_impl->IsRectTopmost(even_rect));
 
   auto* odd_plugin_container_impl =
       To<WebPluginContainerImpl>(GetWebPluginContainer(
           web_view, WebString::FromUTF8("odd-dimensions-plugin")));
   odd_plugin_container_impl->SetFrameRect(gfx::Rect(0, 0, 300, 300));
-  auto odd_rect = odd_plugin_container_impl->GetElement().BoundsInViewport();
+  auto odd_rect = odd_plugin_container_impl->GetElement().BoundsInWidget();
   EXPECT_TRUE(odd_plugin_container_impl->IsRectTopmost(odd_rect));
 }
 
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
index 43645f1a..e0dad52 100644
--- a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
@@ -207,14 +207,6 @@
     case WebFeature::kRTCConstraintEnableDtlsSrtpTrue:
       return DeprecationInfo::WithTranslation(
           feature, DeprecationIssueType::kRTCConstraintEnableDtlsSrtpTrue);
-    case WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics:
-      return DeprecationInfo::WithTranslation(
-          feature,
-          DeprecationIssueType::
-              kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics);
-    case WebFeature::kRTCPeerConnectionSdpSemanticsPlanB:
-      return DeprecationInfo::WithTranslation(
-          feature, DeprecationIssueType::kRTCPeerConnectionSdpSemanticsPlanB);
     case WebFeature::kRtcpMuxPolicyNegotiate:
       return DeprecationInfo::WithTranslation(
           feature, DeprecationIssueType::kRtcpMuxPolicyNegotiate);
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index dc9db2aa..69081a7 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -1671,7 +1671,7 @@
   EXPECT_EQ(0, visual_viewport.GetScrollOffset().y());
 }
 
-TEST_P(VisualViewportTest, ElementBoundsInViewportSpaceAccountsForViewport) {
+TEST_P(VisualViewportTest, ElementBoundsInWidgetSpaceAccountsForViewport) {
   InitializeWithAndroidSettings();
 
   WebView()->MainFrameViewWidget()->Resize(gfx::Size(500, 800));
@@ -1690,7 +1690,7 @@
   visual_viewport.SetScale(2);
   visual_viewport.SetLocation(gfx::PointAtOffsetFromOrigin(scroll_delta));
 
-  const gfx::Rect bounds_in_viewport = input_element->BoundsInViewport();
+  const gfx::Rect bounds_in_viewport = input_element->BoundsInWidget();
   gfx::Rect expected_bounds = gfx::ScaleToRoundedRect(bounds, 2.f);
   gfx::Vector2dF expected_scroll_delta = scroll_delta;
   expected_scroll_delta.Scale(2.f, 2.f);
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 d819059c..fab1c43 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -5761,7 +5761,7 @@
 }
 
 static gfx::Rect ElementBounds(WebLocalFrame* frame, const WebString& id) {
-  return gfx::Rect(frame->GetDocument().GetElementById(id).BoundsInViewport());
+  return gfx::Rect(frame->GetDocument().GetElementById(id).BoundsInWidget());
 }
 
 static std::string SelectionAsString(WebFrame* frame) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index e6f64255..95c55e7 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -544,7 +544,7 @@
   // the focused element is editable to continue writing.
   if (IsElementNotNullAndEditable(focused_element)) {
     std::move(callback).Run(
-        focused_element->BoundsInViewport(),
+        focused_element->BoundsInWidget(),
         frame->View()->FrameToViewport(GetAbsoluteCaretBounds()));
     return;
   }
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni
index abf6554d..c5298eb 100644
--- a/third_party/blink/renderer/core/html/build.gni
+++ b/third_party/blink/renderer/core/html/build.gni
@@ -606,6 +606,8 @@
   "parser/html_preload_scanner.h",
   "parser/background_html_scanner.cc",
   "parser/background_html_scanner.h",
+  "parser/background_html_token_producer.cc",
+  "parser/background_html_token_producer.h",
   "parser/html_resource_preloader.cc",
   "parser/html_resource_preloader.h",
   "parser/html_srcset_parser.cc",
@@ -614,6 +616,8 @@
   "parser/html_token.h",
   "parser/html_tokenizer.cc",
   "parser/html_tokenizer.h",
+  "parser/html_token_producer.cc",
+  "parser/html_token_producer.h",
   "parser/html_tree_builder.cc",
   "parser/html_tree_builder.h",
   "parser/html_view_source_parser.cc",
@@ -628,6 +632,8 @@
   "parser/preload_request.h",
   "parser/resource_preloader.cc",
   "parser/resource_preloader.h",
+  "parser/special_sequences_tracker.cc",
+  "parser/special_sequences_tracker.h",
   "parser/tag_parsing_group.h",
   "parser/text_document_parser.cc",
   "parser/text_document_parser.h",
@@ -717,12 +723,14 @@
   "parser/html_parser_metrics_test.cc",
   "parser/html_preload_scanner_document_test.cc",
   "parser/html_preload_scanner_test.cc",
+  "parser/html_token_producer_test.cc",
   "parser/background_html_scanner_test.cc",
   "parser/html_resource_preloader_test.cc",
   "parser/html_srcset_parser_test.cc",
   "parser/html_tree_builder_test.cc",
   "parser/html_tokenizer_test.cc",
   "parser/html_view_source_parser_test.cc",
+  "parser/special_sequences_tracker_test.cc",
   "parser/text_resource_decoder_builder_test.cc",
   "parser/text_resource_decoder_test.cc",
   "portal/html_portal_element_test.cc",
diff --git a/third_party/blink/renderer/core/html/parser/background_html_token_producer.cc b/third_party/blink/renderer/core/html/parser/background_html_token_producer.cc
new file mode 100644
index 0000000..dab21663
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/background_html_token_producer.cc
@@ -0,0 +1,385 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/parser/background_html_token_producer.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/core/html/parser/html_document_parser.h"
+#include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
+#include "third_party/blink/renderer/core/html_element_lookup_trie.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+
+namespace blink {
+namespace {
+
+// Cached value of `kThreadedHtmlTokenizerTokenMaxCount`.
+wtf_size_t g_max_tokens = 0;
+
+}  // namespace
+
+BackgroundHTMLTokenProducer::BackgroundHTMLTokenProducer(
+    const SegmentedString& input,
+    std::unique_ptr<HTMLTokenizer> tokenizer,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    : input_(input),
+      tokenizer_(std::move(tokenizer)),
+      task_runner_(std::move(task_runner)) {
+  token_ = std::make_unique<HTMLToken>();
+  if (g_max_tokens == 0)
+    g_max_tokens = features::kThreadedHtmlTokenizerTokenMaxCount.Get();
+  // `g_max_tokens` must be > 0, otherwise no tokens will be added and this
+  // code is likely to get stuck.
+  DCHECK_GT(g_max_tokens, 0u);
+  PostCrossThreadTask(
+      *task_runner_, FROM_HERE,
+      CrossThreadBindOnce(
+          &BackgroundHTMLTokenProducer::RunTokenizeLoopOnTaskRunner,
+          CrossThreadUnretained(this)));
+}
+
+BackgroundHTMLTokenProducer::~BackgroundHTMLTokenProducer() {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+
+  // `tokenizer_` keeps a reference to `token_`, delete `tokenizer_` first to
+  // ensure `token_` is still valid.
+  tokenizer_.reset();
+
+  {
+    base::AutoLock auto_lock(results_lock_);
+    if (input_generation_ == processed_input_generation_) {
+      // When the end of input is reached the background thread doesn't call
+      // UpdateHistogramRelatedTotals() (because no tokens are generated).
+      UpdateHistogramRelatedTotals();
+    }
+  }
+
+  // Empty documents may generate a single token, don't log in this case.
+  if (total_tokens_processed_ + total_tokens_processed_when_end_reached_ > 1) {
+    if (num_calls_to_next_parse_results_ > 0) {
+      base::UmaHistogramCounts10000(
+          "Blink.BackgroundTokenizer.AverageTokensAvailablePerCall",
+          total_tokens_processed_ / num_calls_to_next_parse_results_);
+    }
+    if (num_calls_to_next_parse_results_when_end_reached_ > 0) {
+      base::UmaHistogramCounts10000(
+          "Blink.BackgroundTokenizer."
+          "AverageTokensAvailablePerCallWhenEndOfInputReached",
+          total_tokens_processed_when_end_reached_ /
+              num_calls_to_next_parse_results_when_end_reached_);
+    }
+    // This class should only be deleted when ShutdownAndScheduleDeletion()
+    // is called, which sets `shutdown_reason_`.
+    DCHECK(shutdown_reason_.has_value());
+    base::UmaHistogramBoolean(
+        "Blink.BackgroundTokenizer.DidCompleteSuccessfully",
+        shutdown_reason_ == BackgroundHTMLTokenProducerShutdownReason::kDone);
+  }
+}
+
+void BackgroundHTMLTokenProducer::AppendToEnd(const String& string) {
+  DCHECK(IsRunningOnMainThread());
+  {
+    base::AutoLock auto_lock(input_lock_);
+    strings_to_append_.push_back(string);
+    ++input_generation_;
+  }
+  data_available_.Signal();
+}
+
+void BackgroundHTMLTokenProducer::MarkEndOfFile() {
+  DCHECK(IsRunningOnMainThread());
+  {
+    base::AutoLock auto_lock(input_lock_);
+    end_of_file_ = true;
+    ++input_generation_;
+  }
+  data_available_.Signal();
+}
+
+void BackgroundHTMLTokenProducer::ShutdownAndScheduleDeletion(
+    BackgroundHTMLTokenProducerShutdownReason reason) {
+  DCHECK(IsRunningOnMainThread());
+  {
+    base::AutoLock auto_lock(input_lock_);
+    stop_and_delete_ = true;
+    shutdown_reason_ = reason;
+  }
+  data_available_.Signal();
+
+  // Also signal `clear_results_before_next_append_` for the scenario of
+  // background thread waiting for main thread to consume results.
+  {
+    base::AutoLock auto_lock(results_lock_);
+    clear_results_before_next_append_ = true;
+    clear_results_was_set_.Signal();
+  }
+
+  // Use a post task for deletion to ensure background task runner doesn't
+  // delete this part way through.
+  PostCrossThreadTask(
+      *task_runner_, FROM_HERE,
+      CrossThreadBindOnce(&BackgroundHTMLTokenProducer::DeleteOnTaskRunner,
+                          CrossThreadUnretained(this)));
+}
+
+BackgroundHTMLTokenProducer::Results*
+BackgroundHTMLTokenProducer::NextParseResults() {
+  DCHECK(IsRunningOnMainThread());
+  Results* results;
+  while (true) {
+    base::AutoLock auto_lock(results_lock_);
+    // If `clear_results_before_next_append_` is true, the background thread
+    // hasn't yet cleared the results, so need to continue waiting.
+    if (bg_thread_results_.IsEmpty() || clear_results_before_next_append_) {
+      if (input_generation_ == processed_input_generation_) {
+        // Background thread finished parsing all the data, no more results
+        // will be produced until more data is available.
+        return nullptr;
+      }
+      results_available_.Wait();
+      continue;
+    }
+    // Swap the two buffers. The background thread will clear the vector before
+    // adding any new results. Using swap and not clearing the vector enables
+    // destruction to happen on the background thread, this ensures the main
+    // thread does not do costly destruction.
+    main_thread_results_.swap(bg_thread_results_);
+    clear_results_before_next_append_ = true;
+    results = &main_thread_results_;
+    break;
+  }
+  // The background thread blocks when it has processed `g_max_tokens`, signal
+  // to unblock it.
+  if (results->size() == g_max_tokens)
+    clear_results_was_set_.Signal();
+  // The while loop above blocks until at least one result, and there
+  // shouldn't be more than `g_max_tokens`.
+  DCHECK(!results->IsEmpty() && results->size() <= g_max_tokens);
+  return results;
+}
+
+void BackgroundHTMLTokenProducer::RunTokenizeLoopOnTaskRunner() {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  while (true) {
+    // Always try to apply data from the main thread.
+    const ApplyDataResult apply_result = ApplyDataFromMainThread();
+    if (apply_result.stop_and_delete_this)
+      return;
+
+    if (!in_progress_token_data_.has_value()) {
+      in_progress_token_data_.emplace(InProgressTokenData{
+          .string_length_at_start_of_token = input_.length(),
+          .line_count_at_start_of_token = input_.CurrentLine().ZeroBasedInt()});
+    }
+    if (!tokenizer_->NextToken(input_, *token_)) {
+      // Let main thread know reached end of current input.
+      NotifyEndOfInput(apply_result.input_generation);
+
+      // Wait for more data.
+      base::AutoLock auto_lock(input_lock_);
+      while (!end_of_file_ && !stop_and_delete_ &&
+             strings_to_append_.IsEmpty()) {
+        data_available_.Wait();
+      }
+    } else {
+      // A token was generated.
+      if (static_cast<unsigned>(input_.NumberOfCharactersConsumed()) >=
+          special_sequences_tracker_.index_of_first_special_sequence()) {
+        // The token spans a sequence that depends upon state from the main
+        // thread. Notify the main thread of this, and then exit.
+        AppendUnhandledSequenceResult();
+        return;
+      }
+      bool was_tokenizer_state_change_speculative = false;
+      HTMLTokenizer::State state_before_speculative_state_change =
+          HTMLTokenizer::kDataState;
+      if (token_->GetType() == HTMLToken::kStartTag &&
+          token_->GetName().size() > 0) {
+        auto html_tag =
+            lookupHTMLTag(token_->GetName().data(), token_->GetName().size());
+        auto speculative_state = tokenizer_->SpeculativeStateForTag(html_tag);
+        if (speculative_state && speculative_state != tokenizer_->GetState()) {
+          state_before_speculative_state_change = tokenizer_->GetState();
+          tokenizer_->SetState(*speculative_state);
+          was_tokenizer_state_change_speculative = true;
+        }
+      }
+      AppendTokenResult(was_tokenizer_state_change_speculative,
+                        state_before_speculative_state_change);
+    }
+  }
+}
+
+BackgroundHTMLTokenProducer::ApplyDataResult
+BackgroundHTMLTokenProducer::ApplyDataFromMainThread() {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  ApplyDataResult result;
+  WTF::Vector<WTF::String> strings_to_append;
+  bool end_of_file = false;
+  {
+    base::AutoLock auto_lock(input_lock_);
+    std::swap(strings_to_append, strings_to_append_);
+    end_of_file = end_of_file_;
+    end_of_file_ = false;
+    result.stop_and_delete_this = stop_and_delete_;
+    result.input_generation = input_generation_;
+  }
+  if (result.stop_and_delete_this)
+    return result;
+
+  for (const auto& string : strings_to_append) {
+    if (in_progress_token_data_.has_value()) {
+      in_progress_token_data_->string_length_at_start_of_token +=
+          string.length();
+    }
+
+    special_sequences_tracker_.UpdateIndices(string);
+
+    input_.Append(SegmentedString(string));
+  }
+
+  if (end_of_file) {
+    input_.Append(SegmentedString(String(&kEndOfFileMarker, 1)));
+    input_.Close();
+  }
+  return result;
+}
+
+void BackgroundHTMLTokenProducer::DeleteOnTaskRunner() {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  delete this;
+}
+
+void BackgroundHTMLTokenProducer::AppendTokenResult(
+    bool was_tokenizer_state_change_speculative,
+    HTMLTokenizer::State state_before_speculative_state_change) {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  DCHECK(in_progress_token_data_.has_value());
+
+  const unsigned num_chars_processed =
+      in_progress_token_data_->string_length_at_start_of_token -
+      input_.length();
+  const unsigned num_lines_processed =
+      input_.CurrentLine().ZeroBasedInt() -
+      in_progress_token_data_->line_count_at_start_of_token;
+  const unsigned column_position_at_end = input_.CurrentColumn().ZeroBasedInt();
+  in_progress_token_data_.reset();
+
+  AppendResultAndNotify(std::move(token_), num_chars_processed,
+                        num_lines_processed, column_position_at_end,
+                        was_tokenizer_state_change_speculative,
+                        state_before_speculative_state_change);
+  token_ = std::make_unique<HTMLToken>();
+}
+
+void BackgroundHTMLTokenProducer::AppendResultAndNotify(
+    std::unique_ptr<HTMLToken> token,
+    unsigned num_chars_processed,
+    unsigned num_lines_processed,
+    int column_position_at_end,
+    bool was_tokenizer_state_change_speculative,
+    HTMLTokenizer::State state_before_speculative_state_change) {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  bool signal;
+  {
+    base::AutoLock auto_lock(results_lock_);
+    if (clear_results_before_next_append_) {
+      UpdateHistogramRelatedTotals();
+      // Use Shrink() rather than clear() to keep the backing buffer available.
+      // This is done to avoid memory churn during production.
+      bg_thread_results_.Shrink(0u);
+      clear_results_before_next_append_ = false;
+      end_of_input_bg_thread_result_size_ = kNotFound;
+    }
+    bg_thread_results_.push_back(BackgroundHTMLTokenProducerParseResult());
+    BackgroundHTMLTokenProducerParseResult& result = bg_thread_results_.back();
+    result.token = std::move(token);
+    result.num_chars_processed = num_chars_processed;
+    result.num_lines_processed = num_lines_processed;
+    result.column_position_at_end = column_position_at_end;
+    result.was_tokenizer_state_change_speculative =
+        was_tokenizer_state_change_speculative;
+    result.state_before_speculative_state_change =
+        state_before_speculative_state_change;
+
+    if (result.token) {
+      // When there is a valid token, the snapshot can be obtained from the
+      // tokenizer.
+      tokenizer_->GetSnapshot(result.tokenizer_snapshot);
+    } else if (bg_thread_results_.size() > 1) {
+      // This case is hit when this function is called without a token (such
+      // as an unhandled sequence). Copy the tokenizer state to make the main
+      // thread handling simpler (meaning it can always copy directly from
+      // a result, rather than backtracking).
+      result.tokenizer_snapshot =
+          bg_thread_results_[bg_thread_results_.size() - 2].tokenizer_snapshot;
+    } else if (!main_thread_results_.IsEmpty()) {
+      // Similar to previous case, but this is the first result being added to
+      // `bg_thread_results_`. In this case the last snapshot is available in
+      // `main_thread_results_`.
+      result.tokenizer_snapshot =
+          main_thread_results_.back().tokenizer_snapshot;
+    } else {
+      // This is the very first result. In this case no tokens have been
+      // produced, so only the state from the tokenizer is needed.
+      result.tokenizer_snapshot.state = tokenizer_->GetState();
+    }
+
+    // The main thread may be blocked waiting for a token. Signal to wake it up.
+    signal = bg_thread_results_.size() == 1;
+
+    // When adding the max token, wait for the main thread to swap the buffers.
+    while (!clear_results_before_next_append_ &&
+           bg_thread_results_.size() == g_max_tokens) {
+      clear_results_was_set_.Wait();
+    }
+  }
+  if (signal)
+    results_available_.Signal();
+}
+
+void BackgroundHTMLTokenProducer::NotifyEndOfInput(uint8_t input_generation) {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  base::AutoLock auto_lock(results_lock_);
+  processed_input_generation_ = input_generation;
+  results_available_.Signal();
+  end_of_input_bg_thread_result_size_ =
+      bg_thread_results_.IsEmpty() ? kNotFound : bg_thread_results_.size();
+}
+
+void BackgroundHTMLTokenProducer::AppendUnhandledSequenceResult() {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  AppendResultAndNotify(nullptr, 0, 0, 0, false, HTMLTokenizer::kDataState);
+}
+
+void BackgroundHTMLTokenProducer::UpdateHistogramRelatedTotals() {
+  DCHECK(IsRunningOnBackgroundTaskRunner());
+  // NOTE: this code path is hit right after the main thread swapped the
+  // two vectors (`bg_thread_results_` and `main_thread_results_`). This
+  // means the main thread is now processing `main_thread_results_`.
+  const bool was_end_of_input_token =
+      end_of_input_bg_thread_result_size_ == main_thread_results_.size();
+  if (was_end_of_input_token) {
+    total_tokens_processed_when_end_reached_ += main_thread_results_.size();
+    ++num_calls_to_next_parse_results_when_end_reached_;
+  } else {
+    total_tokens_processed_ += main_thread_results_.size();
+    ++num_calls_to_next_parse_results_;
+  }
+}
+
+bool BackgroundHTMLTokenProducer::IsRunningOnMainThread() const {
+  return !task_runner_->RunsTasksInCurrentSequence();
+}
+
+bool BackgroundHTMLTokenProducer::IsRunningOnBackgroundTaskRunner() const {
+  return task_runner_->RunsTasksInCurrentSequence();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/background_html_token_producer.h b/third_party/blink/renderer/core/html/parser/background_html_token_producer.h
new file mode 100644
index 0000000..b7cd332
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/background_html_token_producer.h
@@ -0,0 +1,279 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_BACKGROUND_HTML_TOKEN_PRODUCER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_BACKGROUND_HTML_TOKEN_PRODUCER_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/html/parser/html_token.h"
+#include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
+#include "third_party/blink/renderer/core/html/parser/special_sequences_tracker.h"
+#include "third_party/blink/renderer/platform/text/segmented_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace WTF {
+class String;
+}
+
+namespace blink {
+
+struct HTMLTokenizerSnapshot;
+
+// A single result from the background producer:
+// . A valid HTMLToken, in which case `token` is non-null.
+// . A state was encountered the background producer can not handle (because it
+//   needs state from the tree builder). In this case `token` is null and
+//   `tokenizer_snapshot` contains the snapshot from the last token.
+struct BackgroundHTMLTokenProducerParseResult {
+  USING_FAST_MALLOC(BackgroundHTMLTokenProducerParseResult);
+
+ public:
+  // See class description for details.
+  std::unique_ptr<HTMLToken> token;
+
+  // Captures state from SegmentedString (see SegmentedString for details on
+  // these). These are only set if `token` is non-null.
+  unsigned num_chars_processed;
+  unsigned num_lines_processed;
+  int column_position_at_end;
+
+  // Captures the state of the tokenizer after the token was produced.
+  HTMLTokenizerSnapshot tokenizer_snapshot;
+
+  // True if the tokenizer state was changed because SpeculativeStateForTag()
+  // returned a value that differs from the HTMLTokenizer's state.
+  bool was_tokenizer_state_change_speculative;
+
+  // If `was_tokenizer_state_change_speculative` is set, this gives the original
+  // state of the tokenizer.
+  HTMLTokenizer::State state_before_speculative_state_change;
+};
+
+// Captures why background token production was stopped.
+enum class BackgroundHTMLTokenProducerShutdownReason {
+  // The end was reached.
+  kDone,
+
+  // document.write() was called.
+  kDocumentWrite,
+
+  // The state from the tree builder did not match the speculative state.
+  kStateMismatch,
+
+  // A sequence was encountered that requires state that only the tree builder
+  // knows.
+  kSpecialSequence,
+};
+
+// Class responsible for generating HTMLTokens in a background thread. Token
+// production is done as soon as content is available. Internally this class
+// keeps two vectors with the token results. As tokens are produced they are
+// added to one vector, and another vector with the current results the main
+// is using. A limited number of tokens are produced at a time. The expected
+// use case is to add content (AppendToEnd()) and call NextParseResults() to
+// get the parsed results. NextParseResults() always returns the next set of
+// tokens until the end of content is reached, in which case null is returned.
+//
+// As this class runs on two threads, the destructor is private and deletion
+// only happens once ShutdownAndScheduleDeletion() is called.
+class CORE_EXPORT BackgroundHTMLTokenProducer {
+  USING_FAST_MALLOC(BackgroundHTMLTokenProducer);
+
+ public:
+  BackgroundHTMLTokenProducer(
+      const SegmentedString& input,
+      std::unique_ptr<HTMLTokenizer> tokenizer,
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
+  BackgroundHTMLTokenProducer(const BackgroundHTMLTokenProducer&) = delete;
+  BackgroundHTMLTokenProducer& operator=(const BackgroundHTMLTokenProducer&) =
+      delete;
+
+  using Results = WTF::Vector<BackgroundHTMLTokenProducerParseResult>;
+
+  // Adds new content to parse. This signals the background thread to start
+  // parsing.
+  void AppendToEnd(const String& string);
+
+  // Signals no more data will be available.
+  void MarkEndOfFile();
+
+  // Schedules shutdown and deletes this (deletion happens on the background
+  // thread).
+  void ShutdownAndScheduleDeletion(
+      BackgroundHTMLTokenProducerShutdownReason reason);
+
+  // Returns the next set of results. This returns null if the end of input has
+  // been reached, otherwise it returns a non-null value of the current results
+  // that have been produced. The return value is owned by this class, and valid
+  // until NextParseResults() is called and returns a non-null value.
+  //
+  // Note that this internally blocks until tokens have been produced.
+  Results* NextParseResults();
+
+ private:
+  // Results of calling ApplyDataFromMainThread().
+  struct ApplyDataResult {
+    STACK_ALLOCATED();
+
+   public:
+    // `input_generation_` at the time ApplyDataFromMainThread() was called.
+    uint8_t input_generation = 0;
+
+    // True if ShutdownAndScheduleDeletion() was called.
+    bool stop_and_delete_this = false;
+  };
+
+  ~BackgroundHTMLTokenProducer();
+
+  // Responsible for generating tokens until ShutdownAndScheduleDeletion() is
+  // called.
+  void RunTokenizeLoopOnTaskRunner();
+
+  // Deletes this on the background thread. Called as a result of
+  // ShutdownAndScheduleDeletion().
+  void DeleteOnTaskRunner();
+
+  // Applies data from the main thread to the background thread.
+  ApplyDataResult ApplyDataFromMainThread();
+
+  // Adds a token result and notifies the main thread data is available.
+  void AppendTokenResult(
+      bool was_tokenizer_state_change_speculative,
+      HTMLTokenizer::State state_before_speculative_state_change);
+
+  // Notifies the main thread the end of input has been reached.
+  void NotifyEndOfInput(uint8_t input_generation);
+
+  // Adds a result indicating an unhandled sequence was encountered.
+  void AppendUnhandledSequenceResult();
+
+  // Called internally to add a result and notify the main thread.
+  void AppendResultAndNotify(
+      std::unique_ptr<HTMLToken> token,
+      unsigned num_chars_processed,
+      unsigned num_lines_processed,
+      int column_position_at_end,
+      bool was_tokenizer_state_change_speculative,
+      HTMLTokenizer::State state_before_speculative_state_change);
+
+  void UpdateHistogramRelatedTotals() EXCLUSIVE_LOCKS_REQUIRED(&results_lock_);
+
+  bool IsRunningOnMainThread() const;
+
+  bool IsRunningOnBackgroundTaskRunner() const;
+
+  // This lock is generally used for data that flows from the main thread to the
+  // background.
+  base::Lock input_lock_;
+
+  // Signals data from the main thread is available to the background thread.
+  base::ConditionVariable data_available_{&input_lock_};
+
+  // Data from AppendToEnd() is added here. The background thread adds these
+  // to `input_` (which is used by the tokenizer).
+  WTF::Vector<String> strings_to_append_ GUARDED_BY(input_lock_);
+
+  // Set once MarkEndOfFile() is called
+  bool end_of_file_ GUARDED_BY(input_lock_) = false;
+
+  // Set once ShutdownAndScheduleDeletion() is called.
+  bool stop_and_delete_ GUARDED_BY(input_lock_) = false;
+
+  // A value that increases every time one of AppendToEnd() or MarkEndOfFile()
+  // is called. This is used to detect when the end of input has been reached.
+  // This is modified by the main thread, and read by the background thread.
+  //
+  // Mutation is guarded by `input_lock_` but as it's also read from
+  // NextParseResults(), which is on the critical path, it does not have the
+  // GUARDED_BY annotation.
+  uint8_t input_generation_ = 0;
+
+  // `tokenizer_` operates on this.
+  SegmentedString input_;
+
+  std::unique_ptr<HTMLToken> token_;
+  std::unique_ptr<HTMLTokenizer> tokenizer_;
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // Lock used to guard data generated from the background thread.
+  base::Lock results_lock_;
+
+  // The two vectors of results maintained by this class. The background thread
+  // adds to `bg_thread_results_`. When NextParseResults() is called the main
+  // thread swaps the two vectors and sets `clear_results_before_next_append_`.
+  // The next time the background produces a result it clears
+  // `bg_thread_results_`. Clearing is done on the background thread as it will
+  // free up a bunch of memory, which is expensive and should be avoided on the
+  // main thread.
+  Results main_thread_results_ GUARDED_BY(results_lock_);
+  Results bg_thread_results_ GUARDED_BY(results_lock_);
+  bool clear_results_before_next_append_ GUARDED_BY(results_lock_) = false;
+
+  // Updated by the background thread once it has finished processing input
+  // for the corresponding `input_generation_`.
+  uint8_t processed_input_generation_ GUARDED_BY(results_lock_) = 0;
+
+  // Signals the main thread once results are available.
+  base::ConditionVariable results_available_{&results_lock_};
+
+  // Size of `bg_thread_results_` when end of input was reached. Used for
+  // metrics.
+  wtf_size_t end_of_input_bg_thread_result_size_ = kNotFound;
+
+  // Signals the background thread that `clear_results_before_next_append_`
+  // was set.
+  base::ConditionVariable clear_results_was_set_{&results_lock_};
+
+  // Tracks state for the current token.
+  struct InProgressTokenData {
+    // Length of `input_` when processing started.
+    wtf_size_t string_length_at_start_of_token = 0;
+
+    // Current line of `input_` when processing started.
+    int line_count_at_start_of_token = 0;
+  };
+
+  // Tracks the state of the current token being processed. State is captured
+  // prior to calling Tokenizer::NextToken() and persists if NextToken()
+  // returns null.
+  absl::optional<InProgressTokenData> in_progress_token_data_;
+
+  // Tracks sequences that may not be handled correctly by this class.
+  SpecialSequencesTracker special_sequences_tracker_;
+
+  // The token count and number of calls to NextParseResults() are tracked in
+  // two set of members. This is done to differentiate between when parsing
+  // stopped because of end of input vs when more input is available. The two
+  // are tracked differently so that separate histograms can be recorded.
+
+  // Total number of tokens processed and how many calls to NextParseResults()
+  // it took. This is incremented after NextParseResults(), and only if the
+  // chunks did not contain the last input.
+  unsigned num_calls_to_next_parse_results_ = 0;
+  unsigned total_tokens_processed_ = 0;
+
+  // Number of calls to NextParseResults() and total token count when end of
+  // input was encountered.
+  unsigned num_calls_to_next_parse_results_when_end_reached_ = 0;
+  unsigned total_tokens_processed_when_end_reached_ = 0;
+
+  // Reason supplied to ShutdownAndScheduleDeletion().
+  absl::optional<BackgroundHTMLTokenProducerShutdownReason> shutdown_reason_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_BACKGROUND_HTML_TOKEN_PRODUCER_H_
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 68ef9a1..b7859d71 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -470,18 +470,30 @@
                                        ParserSynchronizationPolicy sync_policy,
                                        ParserPrefetchPolicy prefetch_policy)
     : HTMLDocumentParser(document,
+                         sync_policy,
+                         prefetch_policy,
+                         /* can_use_background_token_producer */ true) {}
+
+HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document,
+                                       ParserSynchronizationPolicy sync_policy,
+                                       ParserPrefetchPolicy prefetch_policy,
+                                       bool can_use_background_token_producer)
+    : HTMLDocumentParser(document,
                          kAllowScriptingContent,
                          sync_policy,
                          prefetch_policy) {
   script_runner_ =
       HTMLParserScriptRunner::Create(ReentryPermit(), &document, this);
 
+  CreateTokenProducer(can_use_background_token_producer);
+
   // Allow declarative shadow DOM for the document parser, if not explicitly
   // disabled.
   bool include_shadow_roots = document.GetDeclarativeShadowRootAllowState() !=
                               Document::DeclarativeShadowRootAllowState::kDeny;
   tree_builder_ = MakeGarbageCollected<HTMLTreeBuilder>(
-      this, document, kAllowScriptingContent, options_, include_shadow_roots);
+      this, document, kAllowScriptingContent, options_, include_shadow_roots,
+      token_producer_.get());
 }
 
 HTMLDocumentParser::HTMLDocumentParser(
@@ -499,15 +511,17 @@
       fragment->GetDocument().GetDeclarativeShadowRootAllowState() ==
       Document::DeclarativeShadowRootAllowState::kAllow;
 
+  // For now document fragment parsing never reports errors.
+  bool report_errors = false;
+  CreateTokenProducer(
+      /* can_use_background_token_producer */ false,
+      TokenizerStateForContextElement(context_element, report_errors,
+                                      options_));
+
   // No script_runner_ in fragment parser.
   tree_builder_ = MakeGarbageCollected<HTMLTreeBuilder>(
       this, fragment, context_element, parser_content_policy, options_,
-      include_shadow_roots);
-
-  // For now document fragment parsing never reports errors.
-  bool report_errors = false;
-  tokenizer_->SetState(TokenizerStateForContextElement(
-      context_element, report_errors, options_));
+      include_shadow_roots, token_producer_.get());
 }
 
 HTMLDocumentParser::HTMLDocumentParser(Document& document,
@@ -516,8 +530,6 @@
                                        ParserPrefetchPolicy prefetch_policy)
     : ScriptableDocumentParser(document, content_policy),
       options_(&document),
-      token_(std::make_unique<HTMLToken>()),
-      tokenizer_(std::make_unique<HTMLTokenizer>(options_)),
       loading_task_runner_(sync_policy == kForceSynchronousParsing
                                ? nullptr
                                : document.GetTaskRunner(TaskType::kNetworking)),
@@ -589,14 +601,9 @@
   insertion_preload_scanner_.reset();
   background_script_scanner_.Reset();
   background_scanner_.Reset();
-  // Oilpan: It is important to clear token_ to deallocate backing memory of
-  // HTMLToken::data_ and let the allocator reuse the memory for
-  // HTMLToken::data_ of a next HTMLDocumentParser. We need to clear
-  // tokenizer_ first because tokenizer_ has a raw pointer to token_.
-  // TODO(masonf): We can probably move tokenizer_ and token_ into the
-  // HTMLDocumentParser itself, instead of having them as Members.
-  tokenizer_.reset();
-  token_.reset();
+  // Oilpan: HTMLTokenProducer may allocate a fair amount of memory. Destroy
+  // it to ensure that memory is released.
+  token_producer_.reset();
 }
 
 void HTMLDocumentParser::StopParsing() {
@@ -617,7 +624,7 @@
   if (IsDetached())
     return;
 
-  DCHECK(tokenizer_);
+  DCHECK(token_producer_);
 
   // NOTE: This pump should only ever emit buffered character tokens.
   if (!GetDocument()->IsPrefetchOnly()) {
@@ -761,14 +768,15 @@
 }
 
 void HTMLDocumentParser::ForcePlaintextForTextDocument() {
-  tokenizer_->SetState(HTMLTokenizer::kPLAINTEXTState);
+  token_producer_->ForcePlaintext();
 }
 
 bool HTMLDocumentParser::PumpTokenizer() {
   DCHECK(!GetDocument()->IsPrefetchOnly());
   DCHECK(!IsStopped());
-  DCHECK(tokenizer_);
-  DCHECK(token_);
+  DCHECK(token_producer_);
+
+  did_pump_tokenizer_ = true;
 
   NestingLevelIncrementer session = task_runner_state_->ScopedPumpSession();
 
@@ -834,6 +842,7 @@
                                                   GetDefaultTimedBudget());
       }
     }
+    HTMLToken* token;
     {
       RUNTIME_CALL_TIMER_SCOPE(
           V8PerIsolateData::MainThreadIsolate(),
@@ -841,16 +850,20 @@
       absl::optional<base::ElapsedTimer> next_token_timer;
       if (metrics_reporter_)
         next_token_timer.emplace();
-      const bool has_next_token =
-          tokenizer_->NextToken(input_.Current(), Token());
+      token = token_producer_->ParseNextToken();
       if (next_token_timer)
         time_in_next_token += next_token_timer->Elapsed();
-      if (!has_next_token)
+      if (!token)
         break;
       budget--;
       tokens_parsed++;
     }
-    ConstructTreeFromHTMLToken();
+    AtomicHTMLToken atomic_html_token(*token);
+    // Clear the HTMLToken in case ConstructTree() synchronously re-enters the
+    // parser. This has to happen after creating AtomicHTMLToken as it needs
+    // state in the HTMLToken.
+    token_producer_->ClearToken();
+    ConstructTreeFromToken(atomic_html_token);
     if (!should_run_until_completion && !IsPaused()) {
       DCHECK_EQ(task_runner_state_->GetMode(), kAllowDeferredParsing);
       if (TimedParserBudgetEnabled())
@@ -867,7 +880,6 @@
     } else {
       should_yield = false;
     }
-    DCHECK(IsStopped() || Token().IsUninitialized());
   }
 
   if (is_tracing) {
@@ -897,7 +909,10 @@
     return false;
 
   if (IsPaused()) {
-    DCHECK_EQ(tokenizer_->GetState(), HTMLTokenizer::kDataState);
+#if DCHECK_IS_ON()
+    DCHECK_EQ(token_producer_->GetCurrentTokenizerState(),
+              HTMLTokenizer::kDataState);
+#endif
 
     if (preloader_ && !background_scanner_) {
       if (!preload_scanner_) {
@@ -954,11 +969,9 @@
       HTMLDocumentParserState::DeferredParserState::kScheduledWithEndIfDelayed);
 }
 
-void HTMLDocumentParser::ConstructTreeFromHTMLToken() {
+void HTMLDocumentParser::ConstructTreeFromToken(AtomicHTMLToken& atomic_token) {
   DCHECK(!GetDocument()->IsPrefetchOnly());
 
-  AtomicHTMLToken atomic_token(Token());
-
   // Check whether we've exited the header.
   if (!task_runner_state_->HaveExitedHeader()) {
     if (GetDocument()->body()) {
@@ -966,10 +979,6 @@
     }
   }
 
-  // We clear the token_ in case ConstructTree() synchronously re-enters the
-  // parser.
-  Token().Clear();
-
   tree_builder_->ConstructTree(&atomic_token);
   CheckIfBlockingStylesheetAdded();
 }
@@ -991,10 +1000,19 @@
   TRACE_EVENT2("blink", "HTMLDocumentParser::insert", "source_length",
                source.length(), "parser", (void*)this);
 
+  const bool was_current_input_empty = input_.Current().IsEmpty();
+
   SegmentedString excluded_line_number_source(source);
   excluded_line_number_source.SetExcludeLineNumbers();
   input_.InsertAtCurrentInsertionPoint(excluded_line_number_source);
 
+  // HTMLTokenProducer may parse the input stream in a background thread.
+  // As the input stream has been modified here, the results from the
+  // background thread are invalid and should be dropped.
+  if (was_current_input_empty && input_.HasInsertionPoint()) {
+    token_producer_->AbortBackgroundParsingForDocumentWrite();
+  }
+
   // Pump the the tokenizer to build the document from the given insert point.
   // Should process everything available and not defer anything.
   ShouldCompleteScope should_complete(task_runner_state_);
@@ -1066,6 +1084,7 @@
   }
 
   input_.AppendToEnd(source);
+  token_producer_->AppendToEnd(input_source);
   task_runner_state_->MarkSeenFirstByte();
 
   // Add input_source.length() to "file size" metric.
@@ -1178,8 +1197,10 @@
   // We're not going to get any more data off the network, so we tell the input
   // stream we've reached the end of file. finish() can be called more than
   // once, if the first time does not call end().
-  if (!input_.HaveSeenEndOfFile())
+  if (!input_.HaveSeenEndOfFile()) {
     input_.MarkEndOfFile();
+    token_producer_->MarkEndOfFile();
+  }
 
   // If there's any deferred work remaining, signal that we
   // want to end the document once all work's complete.
@@ -1249,7 +1270,7 @@
   CheckIfBlockingStylesheetAdded();
   if (IsStopped() || IsPaused() || IsDetached())
     return;
-  DCHECK(tokenizer_);
+  DCHECK(token_producer_);
 
   insertion_preload_scanner_.reset();
   if (task_runner_state_->GetMode() == kAllowDeferredParsing &&
@@ -1582,6 +1603,19 @@
   }
 }
 
+void HTMLDocumentParser::CreateTokenProducer(
+    bool can_use_background_token_producer,
+    HTMLTokenizer::State initial_state) {
+  // HTMLTokenProducer may create a thread; to avoid unnecessary threads being
+  // created only one should be created.
+  DCHECK(!token_producer_);
+  can_use_background_token_producer &=
+      GetDocument()->IsInOutermostMainFrame() &&
+      !task_runner_state_->IsSynchronous();
+  token_producer_ = std::make_unique<HTMLTokenProducer>(
+      input_, options_, can_use_background_token_producer, initial_state);
+}
+
 void HTMLDocumentParser::StartFetchBatch() {
   GetDocument()->Fetcher()->StartBatch();
   pending_batch_operations_++;
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index a9f7900..e70af8f 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -39,7 +39,7 @@
 #include "third_party/blink/renderer/core/html/parser/html_parser_options.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_reentry_permit.h"
 #include "third_party/blink/renderer/core/html/parser/html_preload_scanner.h"
-#include "third_party/blink/renderer/core/html/parser/html_token.h"
+#include "third_party/blink/renderer/core/html/parser/html_token_producer.h"
 #include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
 #include "third_party/blink/renderer/core/html/parser/parser_synchronization_policy.h"
 #include "third_party/blink/renderer/core/html/parser/preload_request.h"
@@ -54,6 +54,7 @@
 
 namespace blink {
 
+class AtomicHTMLToken;
 class BackgroundHTMLScanner;
 class Document;
 class DocumentFragment;
@@ -109,7 +110,7 @@
   // Exposed so that tests can check that the parser's exited in a good state.
   bool HasPendingWorkScheduledForTesting() const;
 
-  HTMLTokenizer* Tokenizer() const { return tokenizer_.get(); }
+  bool DidPumpTokenizerForTesting() const { return did_pump_tokenizer_; }
 
   TextPosition GetTextPosition() const final;
   OrdinalNumber LineNumber() const final;
@@ -121,6 +122,11 @@
   void SetDecoder(std::unique_ptr<TextResourceDecoder>) final;
 
  protected:
+  HTMLDocumentParser(HTMLDocument&,
+                     ParserSynchronizationPolicy,
+                     ParserPrefetchPolicy prefetch_policy,
+                     bool can_use_background_token_producer);
+
   void insert(const String&) final;
   void Append(const String&) override;
   void Finish() final;
@@ -129,6 +135,10 @@
 
   void ForcePlaintextForTextDocument();
 
+  void SetTokenizerState(HTMLTokenizer::State state) {
+    token_producer_->SetTokenizerState(state);
+  }
+
  private:
   HTMLDocumentParser(Document&,
                      ParserContentPolicy,
@@ -170,7 +180,7 @@
   void DeferredPumpTokenizerIfPossible();
   void SchedulePumpTokenizer();
   void ScheduleEndIfDelayed();
-  void ConstructTreeFromHTMLToken();
+  void ConstructTreeFromToken(AtomicHTMLToken& atomic_token);
 
   void RunScriptsForPausedTreeBuilder();
   void ResumeParsingAfterPause();
@@ -212,15 +222,16 @@
     return !pending_preload_data_.IsEmpty();
   }
 
-  HTMLToken& Token() { return *token_; }
+  void CreateTokenProducer(
+      bool can_use_background_token_producer = true,
+      HTMLTokenizer::State initial_state = HTMLTokenizer::kDataState);
 
   const HTMLParserOptions options_;
   HTMLInputStream input_;
   Member<HTMLParserReentryPermit> reentry_permit_ =
       MakeGarbageCollected<HTMLParserReentryPermit>();
 
-  std::unique_ptr<HTMLToken> token_;
-  std::unique_ptr<HTMLTokenizer> tokenizer_;
+  std::unique_ptr<HTMLTokenProducer> token_producer_;
   Member<HTMLParserScriptRunner> script_runner_;
   Member<HTMLTreeBuilder> tree_builder_;
 
@@ -250,6 +261,9 @@
 
   ThreadScheduler* scheduler_;
 
+  // Set to true if PumpTokenizer() was called at least once.
+  bool did_pump_tokenizer_ = false;
+
   // Handle the ref counting of nested batch fetches. Usually the batching is
   // handled by a scope-lock for the duration of the batch (and can be nested
   // within other batches).
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
index f5392e8..c4df487 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
@@ -70,6 +70,21 @@
   bool original_force_synchronous_parsing_for_testing_;
 };
 
+// Calls DocumentParser::Detach() in the destructor. Used to ensure detach is
+// called, as otherwise some assertions may be triggered.
+class ScopedParserDetacher {
+ public:
+  explicit ScopedParserDetacher(DocumentParser* parser) : parser_(parser) {}
+
+  explicit ScopedParserDetacher(HTMLDocumentParser* parser)
+      : ScopedParserDetacher(static_cast<DocumentParser*>(parser)) {}
+
+  ~ScopedParserDetacher() { parser_->Detach(); }
+
+ private:
+  UntracedMember<DocumentParser> parser_;
+};
+
 }  // namespace
 
 INSTANTIATE_TEST_SUITE_P(HTMLDocumentParserTest,
@@ -80,6 +95,7 @@
 TEST_P(HTMLDocumentParserTest, StopThenPrepareToStopShouldNotCrash) {
   auto& document = To<HTMLDocument>(GetDocument());
   DocumentParser* parser = CreateParser(document);
+  ScopedParserDetacher detacher(parser);
   const char kBytes[] = "<html>";
   parser->AppendBytes(kBytes, sizeof(kBytes));
   // These methods are not supposed to be called one after the other, but in
@@ -92,6 +108,7 @@
   auto& document = To<HTMLDocument>(GetDocument());
   HTMLDocumentParser* parser = CreateParser(document);
   DocumentParser* control_parser = static_cast<DocumentParser*>(parser);
+  ScopedParserDetacher detacher(control_parser);
   const char kBytes[] = "<html>";
   control_parser->AppendBytes(kBytes, sizeof(kBytes));
   control_parser->StopParsing();
@@ -102,6 +119,7 @@
   auto& document = To<HTMLDocument>(GetDocument());
   HTMLDocumentParser* parser = CreateParser(document);
   DocumentParser* control_parser = static_cast<DocumentParser*>(parser);
+  ScopedParserDetacher detacher(control_parser);
   const char kBytes1[] = "<html>";
   control_parser->AppendBytes(kBytes1, sizeof(kBytes1));
   control_parser->StopParsing();
@@ -127,6 +145,7 @@
                                *document.GetPage(), true));
   EXPECT_TRUE(document.IsPrefetchOnly());
   HTMLDocumentParser* parser = CreateParser(document);
+  ScopedParserDetacher detacher(parser);
 
   const char kBytes[] = "<httttttt";
   parser->AppendBytes(kBytes, sizeof(kBytes));
@@ -134,14 +153,14 @@
   HTMLParserScriptRunnerHost* script_runner_host =
       parser->AsHTMLParserScriptRunnerHostForTesting();
   EXPECT_TRUE(script_runner_host->HasPreloadScanner());
-  EXPECT_EQ(HTMLTokenizer::kDataState, parser->Tokenizer()->GetState());
   // Finishing should not cause parsing to start (verified via an internal
   // DCHECK).
+  EXPECT_FALSE(parser->DidPumpTokenizerForTesting());
   static_cast<DocumentParser*>(parser)->Finish();
-  EXPECT_EQ(HTMLTokenizer::kDataState, parser->Tokenizer()->GetState());
+  EXPECT_FALSE(parser->DidPumpTokenizerForTesting());
   // Cancel any pending work to make sure that RuntimeFeatures DCHECKs do not
   // fire.
-  (static_cast<DocumentParser*>(parser))->StopParsing();
+  static_cast<DocumentParser*>(parser)->StopParsing();
 }
 
 TEST_P(HTMLDocumentParserTest, AppendNoPrefetch) {
@@ -149,6 +168,7 @@
   EXPECT_FALSE(document.IsPrefetchOnly());
   // Use ForceSynchronousParsing to allow calling append().
   HTMLDocumentParser* parser = CreateParser(document);
+  ScopedParserDetacher detacher(parser);
 
   const char kBytes[] = "<htttttt";
   parser->AppendBytes(kBytes, sizeof(kBytes));
@@ -158,10 +178,10 @@
       parser->AsHTMLParserScriptRunnerHostForTesting();
   EXPECT_EQ(script_runner_host->HasPreloadScanner(),
             GetParam() == kAllowDeferredParsing);
-  EXPECT_EQ(HTMLTokenizer::kTagNameState, parser->Tokenizer()->GetState());
+  EXPECT_TRUE(parser->DidPumpTokenizerForTesting());
   // Cancel any pending work to make sure that RuntimeFeatures DCHECKs do not
   // fire.
-  (static_cast<DocumentParser*>(parser))->StopParsing();
+  static_cast<DocumentParser*>(parser)->StopParsing();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_token_producer.cc b/third_party/blink/renderer/core/html/parser/html_token_producer.cc
new file mode 100644
index 0000000..f719407
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/html_token_producer.cc
@@ -0,0 +1,156 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/parser/html_token_producer.h"
+
+#include "base/feature_list.h"
+#include "base/task/task_traits.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/core/html/parser/background_html_token_producer.h"
+#include "third_party/blink/renderer/core/html/parser/html_document_parser.h"
+#include "third_party/blink/renderer/core/html/parser/html_token.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
+
+namespace blink {
+
+HTMLTokenProducer::HTMLTokenProducer(HTMLInputStream& input_stream,
+                                     const HTMLParserOptions& parser_options,
+                                     bool can_use_background_token_producer,
+                                     HTMLTokenizer::State initial_state)
+    : input_stream_(input_stream),
+      parser_options_(parser_options),
+      initial_state_(initial_state),
+      token_(std::make_unique<HTMLToken>()),
+      tokenizer_(std::make_unique<HTMLTokenizer>(parser_options_)) {
+  tokenizer_->SetState(initial_state);
+  if (can_use_background_token_producer &&
+      base::FeatureList::IsEnabled(features::kThreadedHtmlTokenizer)) {
+    worker_pool_ = worker_pool::CreateSequencedTaskRunner(
+        {base::TaskPriority::USER_BLOCKING, base::WithBaseSyncPrimitives()});
+    // BackgroundHTMLTokenProducer deletes itself when
+    // ShutdownAndScheduleDeletion() is called.
+    background_producer_ = new BackgroundHTMLTokenProducer(
+        {}, std::move(tokenizer_), worker_pool_);
+  }
+}
+
+HTMLTokenProducer::~HTMLTokenProducer() {
+  // Destroy `tokenizer_` first as it keeps a raw pointer to `token_`.
+  tokenizer_.reset();
+  token_.reset();
+
+  if (IsUsingBackgroundProducer()) {
+    background_producer_->ShutdownAndScheduleDeletion(
+        BackgroundHTMLTokenProducerShutdownReason::kDone);
+    background_producer_ = nullptr;
+  }
+}
+
+void HTMLTokenProducer::ForcePlaintext() {
+  // It is assumed that if plaintext is going to be used the constructor was
+  // supplied false for `can_use_background_token_producer`.
+  DCHECK(!IsUsingBackgroundProducer());
+  tokenizer_->SetState(HTMLTokenizer::kPLAINTEXTState);
+}
+
+void HTMLTokenProducer::AbortBackgroundParsingImpl(
+    BackgroundHTMLTokenProducerShutdownReason reason) {
+  if (!IsUsingBackgroundProducer())
+    return;
+
+  tokenizer_ = std::make_unique<HTMLTokenizer>(parser_options_);
+  if (results_ && !results_->IsEmpty()) {
+    // If abort is called after ParseNextToken() when no more data is available,
+    // `current_result_index_` will be > than `current_result_index_`.
+    const wtf_size_t index =
+        std::min(current_result_index_, results_->size() - 1);
+    BackgroundHTMLTokenProducerParseResult& parser_result = (*results_)[index];
+    tokenizer_->RestoreSnapshot(parser_result.tokenizer_snapshot);
+    if (parser_result.was_tokenizer_state_change_speculative)
+      tokenizer_->SetState(parser_result.state_before_speculative_state_change);
+  } else {
+    // Abort was called before the first token is available.
+    tokenizer_->SetState(initial_state_);
+  }
+  tokenizer_->SetForceNullCharacterReplacement(
+      force_null_character_replacement_);
+  tokenizer_->SetShouldAllowCDATA(should_allow_cdata_);
+  background_producer_->ShutdownAndScheduleDeletion(reason);
+  background_producer_ = nullptr;
+  results_ = nullptr;
+}
+
+HTMLToken* HTMLTokenProducer::ParseNextToken() {
+  if (IsUsingBackgroundProducer()) {
+    if (results_ && current_result_index_ < results_->size() &&
+        (!was_tokenizer_state_explicitly_set_ &&
+         CurrentBackgroundProducerResult()
+             .was_tokenizer_state_change_speculative)) {
+      // This is hit if the background producer changed the tokenizer state
+      // but the tree builder did not change the state. When this happens
+      // future background token production is using the wrong state, and could
+      // be wrong. For this reason background production must be stopped.
+      AbortBackgroundParsingImpl(
+          BackgroundHTMLTokenProducerShutdownReason::kStateMismatch);
+    } else {
+      was_tokenizer_state_explicitly_set_ = false;
+      ++current_result_index_;
+      if (!results_ || current_result_index_ >= results_->size()) {
+        auto* next_results = background_producer_->NextParseResults();
+        if (!next_results) {
+          // No more tokens to parse. BackgroundHTMLTokenProducer keeps results
+          // valid until NextParseResults() is called and more input is
+          // available. This way this code always has tokenizer state to restore
+          // (except for initial creation with not enough data for a single
+          // token, but that case doesn't require state to restore).
+          return nullptr;
+        }
+        // The background producer should never return an empty results vector.
+        results_ = next_results;
+        DCHECK(!results_->IsEmpty());
+        current_result_index_ = 0;
+      }
+      BackgroundHTMLTokenProducerParseResult& current_result =
+          CurrentBackgroundProducerResult();
+      if (current_result.token) {
+        AdvanceInput(current_result);
+        return current_result.token.get();
+      }
+      // If the background producer did not provide a token, then a sequence
+      // was encountered that may be treated differently depending upon the
+      // value of `should_allow_cdata_` or `force_null_character_replacement_`.
+      // As the background producer never changes the values of
+      // `should_allow_cdata_` or `force_null_character_replacement_` when a
+      // special sequence is encountered, background production must be stopped.
+      AbortBackgroundParsingImpl(
+          BackgroundHTMLTokenProducerShutdownReason::kSpecialSequence);
+    }
+    // We only get here if background production was aborted and we need to
+    // fall through to using `tokenizer_`.
+    DCHECK(!IsUsingBackgroundProducer());
+  }
+  return tokenizer_->NextToken(input_stream_.Current(), *token_) ? token_.get()
+                                                                 : nullptr;
+}
+
+void HTMLTokenProducer::AppendToEnd(const String& string) {
+  if (IsUsingBackgroundProducer())
+    background_producer_->AppendToEnd(string);
+}
+
+void HTMLTokenProducer::MarkEndOfFile() {
+  if (IsUsingBackgroundProducer())
+    background_producer_->MarkEndOfFile();
+}
+
+void HTMLTokenProducer::AdvanceInput(
+    const BackgroundHTMLTokenProducerParseResult& parse_result) {
+  input_stream_.Current().Advance(parse_result.num_chars_processed,
+                                  parse_result.num_lines_processed,
+                                  parse_result.column_position_at_end);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_token_producer.h b/third_party/blink/renderer/core/html/parser/html_token_producer.h
new file mode 100644
index 0000000..972251a
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/html_token_producer.h
@@ -0,0 +1,193 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_TOKEN_PRODUCER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_TOKEN_PRODUCER_H_
+
+#include <memory>
+
+#include "base/task/sequenced_task_runner.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/html/parser/background_html_token_producer.h"
+#include "third_party/blink/renderer/core/html/parser/html_input_stream.h"
+#include "third_party/blink/renderer/core/html/parser/html_parser_options.h"
+#include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+class String;
+}
+
+namespace blink {
+class HTMLToken;
+class BackgroundHTMLTokenProducer;
+struct BackgroundHTMLTokenProducerParseResult;
+
+// HTMLTokenProducer is responsible for producing HTMLTokens. It may do the
+// production on a background thread by using BackgroundHTMLTokenProducer.
+// Producing tokens on the background thread has a number of limitations, if a
+// scenario is encountered that can not be handled, production switches to
+// running on the foreground thread. See BackgroundHTMLTokenProducer for the
+// specifics.
+//
+// TODO(https://crbug.com/1345267): it probably makes sense that this class
+// owns the inputstream. At a minimum HTMLDocumentParser shouldn't expose
+// InputStream (through HTMLParserScriptRunnerHost).
+class CORE_EXPORT HTMLTokenProducer {
+  USING_FAST_MALLOC(HTMLTokenProducer);
+
+ public:
+  // `can_use_background_token_producer` indicates whether tokens can be
+  // produced on a background thread. Whether a background thread is used is
+  // gated by a feature.
+  HTMLTokenProducer(HTMLInputStream& input_stream,
+                    const HTMLParserOptions& parser_options,
+                    bool can_use_background_token_producer,
+                    HTMLTokenizer::State initial_state);
+  HTMLTokenProducer(const HTMLTokenProducer&) = delete;
+  HTMLTokenProducer& operator=(const HTMLTokenProducer&) = delete;
+  ~HTMLTokenProducer();
+
+  // Forces plaintext. It is assumed this is called early on (before any
+  // tokens have been requested) and that `can_use_background_token_producer`
+  // is false.
+  void ForcePlaintext();
+
+  // Called if a scenario is encountered where production can not be run in
+  // the background. When called production switches to running in the current
+  // thread.
+  void AbortBackgroundParsingForDocumentWrite() {
+    AbortBackgroundParsingImpl(
+        BackgroundHTMLTokenProducerShutdownReason::kDocumentWrite);
+  }
+
+  // Returns the next token. The return value is owned by this class and only
+  // valid until ParseNextToken() is called. This returns null if no more tokens
+  // are available.
+  HTMLToken* ParseNextToken();
+
+  // Clears the token.
+  void ClearToken() {
+    // Background parsing creates a unique token every time, so need to clear
+    // it.
+    if (!IsUsingBackgroundProducer())
+      token_->Clear();
+  }
+
+  // Appends `string` to the end of text to parse.
+  void AppendToEnd(const String& string);
+
+  // Marks the end of the file.
+  void MarkEndOfFile();
+
+  // Returns true if parsing is happening on a background thread.
+  ALWAYS_INLINE bool IsUsingBackgroundProducer() const {
+    return background_producer_ != nullptr;
+  }
+
+#if DCHECK_IS_ON()
+  // This function is really only for assertions.
+  HTMLTokenizer::State GetCurrentTokenizerState() const {
+    if (IsUsingBackgroundProducer()) {
+      DCHECK(results_ && !results_->IsEmpty());
+      return CurrentBackgroundProducerResult().tokenizer_snapshot.state;
+    }
+    return tokenizer_->GetState();
+  }
+#endif
+
+  // These functions are exposed for the tree builder to set tokenizer state.
+  // This code is on critical path, so inlined.
+  ALWAYS_INLINE void SetTokenizerState(HTMLTokenizer::State state) {
+    if (!IsUsingBackgroundProducer()) {
+      tokenizer_->SetState(state);
+      return;
+    }
+    was_tokenizer_state_explicitly_set_ = true;
+    if (state != CurrentBackgroundProducerResult().tokenizer_snapshot.state) {
+      AbortBackgroundParsingImpl(
+          BackgroundHTMLTokenProducerShutdownReason::kStateMismatch);
+      DCHECK(tokenizer_);
+      tokenizer_->SetState(state);
+    }
+  }
+  void SetForceNullCharacterReplacement(bool value) {
+    if (!IsUsingBackgroundProducer()) {
+      tokenizer_->SetForceNullCharacterReplacement(value);
+      return;
+    }
+    force_null_character_replacement_ = value;
+  }
+  void SetShouldAllowCDATA(bool value) {
+    if (!IsUsingBackgroundProducer()) {
+      tokenizer_->SetShouldAllowCDATA(value);
+      return;
+    }
+    should_allow_cdata_ = value;
+  }
+
+ private:
+  // Moves production to the current thread. Does nothing if production already
+  // occurring on the current thread.
+  void AbortBackgroundParsingImpl(
+      BackgroundHTMLTokenProducerShutdownReason reason);
+
+  // Advances the text to parse.
+  void AdvanceInput(const BackgroundHTMLTokenProducerParseResult& parse_result);
+
+  // Returns the current token from the background producer.
+  ALWAYS_INLINE const BackgroundHTMLTokenProducerParseResult&
+  CurrentBackgroundProducerResult() const {
+    DCHECK(results_ && current_result_index_ < results_->size());
+    return (*results_)[current_result_index_];
+  }
+  ALWAYS_INLINE BackgroundHTMLTokenProducerParseResult&
+  CurrentBackgroundProducerResult() {
+    return const_cast<BackgroundHTMLTokenProducerParseResult&>(
+        const_cast<const HTMLTokenProducer*>(this)
+            ->CurrentBackgroundProducerResult());
+  }
+
+  using Results = WTF::Vector<BackgroundHTMLTokenProducerParseResult>;
+
+  // Common state:
+  HTMLInputStream& input_stream_;
+
+  const HTMLParserOptions parser_options_;
+
+  // The initial state for the tokenizer.
+  const HTMLTokenizer::State initial_state_;
+
+  // State used when tokenizer runs on main thread:
+
+  // Used if production happening on the current thread.
+  std::unique_ptr<HTMLToken> token_;
+  std::unique_ptr<HTMLTokenizer> tokenizer_;
+
+  // The remaining state is only used when the tokenizer runs off the main
+  // thread.
+
+  // Set to true if SetTokenizerState() was called.
+  bool was_tokenizer_state_explicitly_set_ = false;
+
+  // TaskRunner the background producer runs on.
+  scoped_refptr<base::SequencedTaskRunner> worker_pool_;
+
+  // This deletes itself once ShutdownAndScheduleDeletion() is called.
+  BackgroundHTMLTokenProducer* background_producer_ = nullptr;
+
+  // Results from the background producer. This is owned by the background
+  // producer and valid until the next call to
+  // BackgroundHTMLTokenProducer::NextParseResults().
+  Results* results_ = nullptr;
+  wtf_size_t current_result_index_ = 0u;
+
+  // State set from the tree builder.
+  bool force_null_character_replacement_ = false;
+  bool should_allow_cdata_ = false;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_TOKEN_PRODUCER_H_
diff --git a/third_party/blink/renderer/core/html/parser/html_token_producer_test.cc b/third_party/blink/renderer/core/html/parser/html_token_producer_test.cc
new file mode 100644
index 0000000..40acd72
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/html_token_producer_test.cc
@@ -0,0 +1,189 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/parser/html_token_producer.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/core/html/parser/atomic_html_token.h"
+#include "third_party/blink/renderer/core/html/parser/html_parser_options.h"
+#include "third_party/blink/renderer/core/html_names.h"
+
+namespace blink {
+
+class HTMLTokenProducerTest : public testing::Test {
+  void SetUp() override {
+    testing::Test::SetUp();
+    scoped_feature_list_.InitAndEnableFeature(features::kThreadedHtmlTokenizer);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(HTMLTokenProducerTest, Basic) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<body>a body");
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+  EXPECT_EQ(html_names::kBodyTag, token->GetName().AsAtomicString());
+
+  token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kCharacter, token->GetType());
+  EXPECT_EQ(String("a body"), token->Characters().AsString());
+
+  // No more tokens, because need end of file.
+  token = producer.ParseNextToken();
+  EXPECT_FALSE(token);
+
+  input_stream.MarkEndOfFile();
+  producer.MarkEndOfFile();
+  token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kEndOfFile, token->GetType());
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+}
+
+TEST_F(HTMLTokenProducerTest, TagSplitAcrossSegments) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<bo");
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  ASSERT_FALSE(producer.ParseNextToken());
+
+  input_stream.AppendToEnd(SegmentedString("dy>"));
+  producer.AppendToEnd(String("dy>"));
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+  EXPECT_EQ(html_names::kBodyTag, token->GetName().AsAtomicString());
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+}
+
+TEST_F(HTMLTokenProducerTest, AbortWithTagSplit) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<bo");
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  ASSERT_FALSE(producer.ParseNextToken());
+
+  producer.AbortBackgroundParsingForDocumentWrite();
+  EXPECT_FALSE(producer.IsUsingBackgroundProducer());
+
+  input_stream.AppendToEnd(SegmentedString("dy>"));
+  producer.AppendToEnd(String("dy>"));
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+  EXPECT_EQ(html_names::kBodyTag, token->GetName().AsAtomicString());
+}
+
+TEST_F(HTMLTokenProducerTest, AbortOnBoundary) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<body>text");
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+  EXPECT_EQ(html_names::kBodyTag, token->GetName().AsAtomicString());
+
+  producer.AbortBackgroundParsingForDocumentWrite();
+  EXPECT_FALSE(producer.IsUsingBackgroundProducer());
+  token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kCharacter, token->GetType());
+  EXPECT_EQ(String("text"), token->Characters().AsString());
+}
+
+TEST_F(HTMLTokenProducerTest, AbortOnNullChar) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<body>t\0ext</body>", 18u);
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+  EXPECT_EQ(html_names::kBodyTag, token->GetName().AsAtomicString());
+
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  producer.ClearToken();
+  token = producer.ParseNextToken();
+  EXPECT_FALSE(producer.IsUsingBackgroundProducer());
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kCharacter, token->GetType());
+  EXPECT_EQ(String("text"), token->Characters().AsString());
+
+  producer.ClearToken();
+  token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kEndTag, token->GetType());
+  EXPECT_EQ(html_names::kBodyTag, token->GetName().AsAtomicString());
+}
+
+TEST_F(HTMLTokenProducerTest, StateMismatch) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<svg><script>x");
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  producer.ClearToken();
+  token = producer.ParseNextToken();
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+  EXPECT_EQ(String("script"), token->GetName().AsString());
+
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  producer.ClearToken();
+  token = producer.ParseNextToken();
+  EXPECT_FALSE(producer.IsUsingBackgroundProducer());
+  ASSERT_TRUE(token);
+  EXPECT_EQ(HTMLToken::kCharacter, token->GetType());
+}
+
+TEST_F(HTMLTokenProducerTest, CurrentColumn) {
+  HTMLInputStream input_stream;
+  HTMLTokenProducer producer(input_stream, HTMLParserOptions(), true,
+                             HTMLTokenizer::kDataState);
+  String string("<body>\nx<div>");
+  input_stream.AppendToEnd(SegmentedString(string));
+  producer.AppendToEnd(string);
+  HTMLToken* token = producer.ParseNextToken();
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kStartTag, token->GetType());
+
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  producer.ClearToken();
+  token = producer.ParseNextToken();
+  EXPECT_TRUE(producer.IsUsingBackgroundProducer());
+  ASSERT_TRUE(token);
+  ASSERT_EQ(HTMLToken::kCharacter, token->GetType());
+  EXPECT_EQ(1, input_stream.Current().CurrentColumn().ZeroBasedInt());
+  EXPECT_EQ(1, input_stream.Current().CurrentLine().ZeroBasedInt());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_tokenizer.cc b/third_party/blink/renderer/core/html/parser/html_tokenizer.cc
index bfe6a5a..fc17b7f 100644
--- a/third_party/blink/renderer/core/html/parser/html_tokenizer.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tokenizer.cc
@@ -204,6 +204,20 @@
   return true;
 }
 
+void HTMLTokenizer::GetSnapshot(HTMLTokenizerSnapshot& snapshot) const {
+  snapshot.state = state_;
+  snapshot.appropriate_end_tag_name = appropriate_end_tag_name_.AsString();
+  snapshot.buffered_end_tag_name = buffered_end_tag_name_.AsString();
+}
+
+void HTMLTokenizer::RestoreSnapshot(const HTMLTokenizerSnapshot& snapshot) {
+  state_ = snapshot.state;
+  appropriate_end_tag_name_.clear();
+  appropriate_end_tag_name_.Append(snapshot.appropriate_end_tag_name);
+  buffered_end_tag_name_.clear();
+  buffered_end_tag_name_.Append(snapshot.buffered_end_tag_name.Span8());
+}
+
 bool HTMLTokenizer::NextToken(SegmentedString& source, HTMLToken& token) {
   // If we have a token in progress, then we're supposed to be called back
   // with the same token so we can finish it.
diff --git a/third_party/blink/renderer/core/html/parser/html_tokenizer.h b/third_party/blink/renderer/core/html/parser/html_tokenizer.h
index 1302e0e..d3eb5af0 100644
--- a/third_party/blink/renderer/core/html/parser/html_tokenizer.h
+++ b/third_party/blink/renderer/core/html/parser/html_tokenizer.h
@@ -36,9 +36,12 @@
 #include "third_party/blink/renderer/core/html/parser/input_stream_preprocessor.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/platform/text/segmented_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
+struct HTMLTokenizerSnapshot;
+
 class CORE_EXPORT HTMLTokenizer {
   USING_FAST_MALLOC(HTMLTokenizer);
 
@@ -127,6 +130,9 @@
     kCDATASectionEndState,
   };
 
+  void GetSnapshot(HTMLTokenizerSnapshot& snapshot) const;
+  void RestoreSnapshot(const HTMLTokenizerSnapshot& snapshot);
+
   // This function returns true if it emits a token. Otherwise, callers
   // must provide the same (in progress) token on the next call (unless
   // they call reset() first).
@@ -177,7 +183,7 @@
   bool ShouldAllowCDATA() const { return should_allow_cdata_; }
   void SetShouldAllowCDATA(bool value) { should_allow_cdata_ = value; }
 
-  State GetState() const { return state_; }
+  ALWAYS_INLINE State GetState() const { return state_; }
   void SetState(State state) { state_ = state; }
 
   inline bool ShouldSkipNullCharacters() const {
@@ -204,6 +210,8 @@
   }
 
  private:
+  friend class HTMLTokenizerTest;
+
   inline bool ProcessEntity(SegmentedString&);
 
   // Returns true if it has skipped all the whitespaces and we still have
@@ -296,6 +304,19 @@
   HTMLParserOptions options_;
 };
 
+// Snapshot of the tokenizers state. Used by HTMLTokenProducer when it switches
+// from producing tokens in the background thread to the main thread.
+struct CORE_EXPORT HTMLTokenizerSnapshot {
+  USING_FAST_MALLOC(HTMLTokenizerSnapshot);
+
+ public:
+  HTMLTokenizer::State state;
+  String appropriate_end_tag_name;
+  String buffered_end_tag_name;
+  // NOTE: This does not include `force_null_character_replacement` and
+  // `should_allow_cdata` as they are never changed in the background tokenizer.
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_TOKENIZER_H_
diff --git a/third_party/blink/renderer/core/html/parser/html_tokenizer_test.cc b/third_party/blink/renderer/core/html/parser/html_tokenizer_test.cc
index 1ba8fce6..c902334 100644
--- a/third_party/blink/renderer/core/html/parser/html_tokenizer_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tokenizer_test.cc
@@ -12,8 +12,31 @@
 
 namespace blink {
 
+class HTMLTokenizerTest : public testing::Test {
+ public:
+  static void SetAppropriateEndTagName(HTMLTokenizer& tokenizer,
+                                       const String& string) {
+    tokenizer.appropriate_end_tag_name_.clear();
+    tokenizer.appropriate_end_tag_name_.Append(string);
+  }
+
+  static String GetAppropriateEndTagName(HTMLTokenizer& tokenizer) {
+    return tokenizer.appropriate_end_tag_name_.AsString();
+  }
+
+  static void SetBufferedEndTagName(HTMLTokenizer& tokenizer,
+                                    const String& string) {
+    tokenizer.buffered_end_tag_name_.clear();
+    tokenizer.buffered_end_tag_name_.Append(string.Span8());
+  }
+
+  static String GetBufferedEndTagName(HTMLTokenizer& tokenizer) {
+    return tokenizer.buffered_end_tag_name_.AsString();
+  }
+};
+
 // This is a regression test for crbug.com/619141
-TEST(HTMLTokenizerTest, ZeroOffsetAttributeNameRange) {
+TEST_F(HTMLTokenizerTest, ZeroOffsetAttributeNameRange) {
   HTMLParserOptions options;
   std::unique_ptr<HTMLTokenizer> tokenizer =
       std::make_unique<HTMLTokenizer>(options);
@@ -29,4 +52,25 @@
   EXPECT_FALSE(tokenizer->NextToken(input2, token));
 }
 
+TEST_F(HTMLTokenizerTest, SaveAndRestoreSnapshot) {
+  HTMLParserOptions options;
+  HTMLTokenizer tokenizer(options);
+  String end_tag_name("end-tag");
+  String buffered_end_tag_name("buffered-end-tag");
+  SetAppropriateEndTagName(tokenizer, end_tag_name);
+  SetBufferedEndTagName(tokenizer, buffered_end_tag_name);
+  HTMLTokenizerSnapshot snapshot;
+  tokenizer.GetSnapshot(snapshot);
+
+  HTMLTokenizer tokenizer2(options);
+  tokenizer2.RestoreSnapshot(snapshot);
+  EXPECT_EQ(end_tag_name, GetAppropriateEndTagName(tokenizer2));
+  EXPECT_EQ(buffered_end_tag_name, GetBufferedEndTagName(tokenizer2));
+
+  // Append an empty snapshot, which should clear the data.
+  tokenizer2.RestoreSnapshot(HTMLTokenizerSnapshot());
+  EXPECT_TRUE(GetAppropriateEndTagName(tokenizer2).IsEmpty());
+  EXPECT_TRUE(GetBufferedEndTagName(tokenizer2).IsEmpty());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
index e51918b8..fc527ba 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html/parser/html_stack_item.h"
 #include "third_party/blink/renderer/core/html/parser/html_token.h"
+#include "third_party/blink/renderer/core/html/parser/html_token_producer.h"
 #include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
 #include "third_party/blink/renderer/core/html/parser/tag_parsing_group.h"
 #include "third_party/blink/renderer/core/html_names.h"
@@ -213,7 +214,8 @@
                                  Document& document,
                                  ParserContentPolicy parser_content_policy,
                                  const HTMLParserOptions& options,
-                                 bool include_shadow_roots)
+                                 bool include_shadow_roots,
+                                 HTMLTokenProducer* token_producer)
     : frameset_ok_(true),
       tree_(parser->ReentryPermit(), document, parser_content_policy),
       insertion_mode_(kInitialMode),
@@ -222,19 +224,24 @@
       include_shadow_roots_(include_shadow_roots),
       parser_(parser),
       script_to_process_start_position_(UninitializedPositionValue1()),
-      options_(options) {}
+      options_(options),
+      token_producer_(token_producer) {
+  DCHECK(token_producer);
+}
 
 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser,
                                  DocumentFragment* fragment,
                                  Element* context_element,
                                  ParserContentPolicy parser_content_policy,
                                  const HTMLParserOptions& options,
-                                 bool include_shadow_roots)
+                                 bool include_shadow_roots,
+                                 HTMLTokenProducer* token_producer)
     : HTMLTreeBuilder(parser,
                       fragment->GetDocument(),
                       parser_content_policy,
                       options,
-                      include_shadow_roots) {
+                      include_shadow_roots,
+                      token_producer) {
   DCHECK(IsMainThread());
   DCHECK(context_element);
   tree_.InitFragmentParsing(fragment, context_element);
@@ -320,9 +327,9 @@
         !HTMLElementStack::IsMathMLTextIntegrationPoint(adjusted_current_node);
   }
 
-  parser_->Tokenizer()->SetForceNullCharacterReplacement(
+  token_producer_->SetForceNullCharacterReplacement(
       insertion_mode_ == kTextMode || in_foreign_content);
-  parser_->Tokenizer()->SetShouldAllowCDATA(in_foreign_content);
+  token_producer_->SetShouldAllowCDATA(in_foreign_content);
 
   tree_.ExecuteQueuedTasks();
   // We might be detached now.
@@ -677,7 +684,7 @@
     case TagParsingGroup::kPlaintextTag:
       ProcessFakePEndTagIfPInButtonScope();
       tree_.InsertHTMLElement(token);
-      parser_->Tokenizer()->SetState(HTMLTokenizer::kPLAINTEXTState);
+      token_producer_->SetTokenizerState(HTMLTokenizer::kPLAINTEXTState);
       break;
     case TagParsingGroup::kATag: {
       Element* active_a_tag =
@@ -751,7 +758,7 @@
     case TagParsingGroup::kTextareaTag:
       tree_.InsertHTMLElement(token);
       should_skip_leading_newline_ = true;
-      parser_->Tokenizer()->SetState(HTMLTokenizer::kRCDATAState);
+      token_producer_->SetTokenizerState(HTMLTokenizer::kRCDATAState);
       original_insertion_mode_ = insertion_mode_;
       frameset_ok_ = false;
       SetInsertionMode(kTextMode);
@@ -2165,7 +2172,7 @@
 
         // We must set the tokenizer's state to DataState explicitly if the
         // tokenizer didn't have a chance to.
-        parser_->Tokenizer()->SetState(HTMLTokenizer::kDataState);
+        token_producer_->SetTokenizerState(HTMLTokenizer::kDataState);
         return;
       }
       tree_.OpenElements()->Pop();
@@ -2698,7 +2705,7 @@
 void HTMLTreeBuilder::ProcessGenericRCDATAStartTag(AtomicHTMLToken* token) {
   DCHECK_EQ(token->GetType(), HTMLToken::kStartTag);
   tree_.InsertHTMLElement(token);
-  parser_->Tokenizer()->SetState(HTMLTokenizer::kRCDATAState);
+  token_producer_->SetTokenizerState(HTMLTokenizer::kRCDATAState);
   original_insertion_mode_ = insertion_mode_;
   SetInsertionMode(kTextMode);
 }
@@ -2706,7 +2713,7 @@
 void HTMLTreeBuilder::ProcessGenericRawTextStartTag(AtomicHTMLToken* token) {
   DCHECK_EQ(token->GetType(), HTMLToken::kStartTag);
   tree_.InsertHTMLElement(token);
-  parser_->Tokenizer()->SetState(HTMLTokenizer::kRAWTEXTState);
+  token_producer_->SetTokenizerState(HTMLTokenizer::kRAWTEXTState);
   original_insertion_mode_ = insertion_mode_;
   SetInsertionMode(kTextMode);
 }
@@ -2714,7 +2721,7 @@
 void HTMLTreeBuilder::ProcessScriptStartTag(AtomicHTMLToken* token) {
   DCHECK_EQ(token->GetType(), HTMLToken::kStartTag);
   tree_.InsertScriptElement(token);
-  parser_->Tokenizer()->SetState(HTMLTokenizer::kScriptDataState);
+  token_producer_->SetTokenizerState(HTMLTokenizer::kScriptDataState);
   original_insertion_mode_ = insertion_mode_;
 
   TextPosition position = parser_->GetTextPosition();
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.h b/third_party/blink/renderer/core/html/parser/html_tree_builder.h
index 6122577..09b052a 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.h
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.h
@@ -44,6 +44,7 @@
 class Element;
 class HTMLDocument;
 class HTMLDocumentParser;
+class HTMLTokenProducer;
 
 class HTMLTreeBuilder final : public GarbageCollected<HTMLTreeBuilder> {
  public:
@@ -55,13 +56,15 @@
                   Document&,
                   ParserContentPolicy,
                   const HTMLParserOptions&,
-                  bool include_shadow_roots);
+                  bool include_shadow_roots,
+                  HTMLTokenProducer* token_producer);
   HTMLTreeBuilder(HTMLDocumentParser*,
                   DocumentFragment*,
                   Element* context_element,
                   ParserContentPolicy,
                   const HTMLParserOptions&,
-                  bool include_shadow_roots);
+                  bool include_shadow_roots,
+                  HTMLTokenProducer* token_producer);
   HTMLTreeBuilder(const HTMLTreeBuilder&) = delete;
   HTMLTreeBuilder& operator=(const HTMLTreeBuilder&) = delete;
   ~HTMLTreeBuilder();
@@ -264,6 +267,10 @@
   TextPosition script_to_process_start_position_;
 
   HTMLParserOptions options_;
+
+  // This is owned by HTMLDocumentParser, kept as a member as needed quite
+  // frequently.
+  HTMLTokenProducer* token_producer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/literal_buffer.h b/third_party/blink/renderer/core/html/parser/literal_buffer.h
index b7a5212f..22246898 100644
--- a/third_party/blink/renderer/core/html/parser/literal_buffer.h
+++ b/third_party/blink/renderer/core/html/parser/literal_buffer.h
@@ -12,6 +12,7 @@
 #include "base/bits.h"
 #include "base/check_op.h"
 #include "base/compiler_specific.h"
+#include "base/containers/span.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_encoding.h"
@@ -72,6 +73,18 @@
     *end_++ = val;
   }
 
+  template <typename OtherT>
+  void AppendSpan(const base::span<OtherT>& val) {
+    static_assert(sizeof(T) >= sizeof(OtherT),
+                  "T is not big enough to contain OtherT");
+    size_t count = val.size();
+    size_t new_size = size() + count;
+    if (capacity() < new_size)
+      Grow(new_size);
+    std::copy_n(val.data(), count, end_);
+    end_ += count;
+  }
+
   template <typename OtherT, wtf_size_t kOtherSize>
   void AppendLiteralImpl(const LiteralBufferBase<OtherT, kOtherSize>& val) {
     static_assert(sizeof(T) >= sizeof(OtherT),
@@ -198,6 +211,8 @@
 
   ALWAYS_INLINE void AddChar(LChar val) { this->AddCharImpl(val); }
 
+  void Append(const base::span<const LChar>& span) { this->AppendSpan(span); }
+
   String AsString() const { return String(this->data(), this->size()); }
 };
 
@@ -245,6 +260,15 @@
     this->AppendLiteralImpl(val);
   }
 
+  void Append(const String& string) {
+    if (string.IsEmpty())
+      return;
+    if (string.Is8Bit())
+      this->AppendSpan(string.Span8());
+    else
+      this->AppendSpan(string.Span16());
+  }
+
   String AsString() const {
     if (g_literal_buffer_create_string_with_encoding && Is8Bit())
       return String::Make8BitFrom16BitSource(this->data(), this->size());
diff --git a/third_party/blink/renderer/core/html/parser/literal_buffer_test.cc b/third_party/blink/renderer/core/html/parser/literal_buffer_test.cc
index 866483f40..e3d02a1 100644
--- a/third_party/blink/renderer/core/html/parser/literal_buffer_test.cc
+++ b/third_party/blink/renderer/core/html/parser/literal_buffer_test.cc
@@ -107,6 +107,34 @@
   EXPECT_FALSE(buf2.Is8Bit());
 }
 
+TEST(LiteralBufferTest, UCharAppendSpan) {
+  UCharLiteralBuffer<16> buf;
+  String string8("abc");
+  buf.Append(string8);
+  EXPECT_EQ(string8, buf.AsString());
+
+  String string16 = u"\x01D6";
+  ASSERT_FALSE(string16.Is8Bit());
+  buf.clear();
+  buf.Append(string16);
+  EXPECT_EQ(string16, buf.AsString());
+}
+
+TEST(LiteralBufferTest, LCharAppendSpan) {
+  LCharLiteralBuffer<16> buf;
+  String string8("abc");
+  buf.Append(string8.Span8());
+  EXPECT_EQ(string8, buf.AsString());
+}
+
+TEST(LiteralBufferTest, AsString) {
+  LCharLiteralBuffer<16> buf;
+  buf.AddChar('x');
+  const String as_string = buf.AsString();
+  EXPECT_TRUE(as_string.Is8Bit());
+  EXPECT_EQ("x", as_string);
+}
+
 TEST(LiteralBufferTest, AsStringIs8Bit) {
   LCharLiteralBuffer<2> lit;
   lit.AddChar('a');
diff --git a/third_party/blink/renderer/core/html/parser/special_sequences_tracker.cc b/third_party/blink/renderer/core/html/parser/special_sequences_tracker.cc
new file mode 100644
index 0000000..b47ed01
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/special_sequences_tracker.cc
@@ -0,0 +1,84 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/parser/special_sequences_tracker.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace {
+
+const LChar kCData[] = "<![CDATA[";
+// Don't include the \0 in the count.
+constexpr wtf_size_t kCDataLength =
+    static_cast<wtf_size_t>(std::size(kCData) - 1);
+
+}  // namespace
+
+void SpecialSequencesTracker::UpdateIndices(const String& string) {
+  if (special_sequence_index_ != kNoSpecialSequencesFound)
+    return;
+
+  UpdateIndexOfCDATA(string);
+  special_sequence_index_ =
+      std::min(IndexOfNullChar(string), special_sequence_index_);
+  total_string_length_ += string.length();
+}
+
+unsigned SpecialSequencesTracker::IndexOfNullChar(const String& string) const {
+  const wtf_size_t index = string.find(static_cast<UChar>('\0'));
+  return index == kNotFound ? kNoSpecialSequencesFound
+                            : (index + total_string_length_);
+}
+
+bool SpecialSequencesTracker::MatchPossibleCDataSection(
+    const String& string,
+    wtf_size_t start_string_index) {
+  DCHECK_GT(num_matching_cdata_chars_, 0u);
+  DCHECK_LT(start_string_index, kNotFound);
+  const wtf_size_t string_length = string.length();
+  wtf_size_t i = 0;
+  // Iterate through `string` while it matches `kCData`. i starts at 0, but is
+  // relative to `start_string_index`. `num_matching_cdata_chars_` is the
+  // position into `kCData` to start the match from (portion of previous string
+  // that matched).
+  while (i + num_matching_cdata_chars_ < kCDataLength &&
+         (i + start_string_index) < string_length &&
+         string[i + start_string_index] ==
+             kCData[i + num_matching_cdata_chars_]) {
+    ++i;
+  }
+  if (i + num_matching_cdata_chars_ == kCDataLength) {
+    // Matched all cdata.
+    special_sequence_index_ =
+        total_string_length_ + i + start_string_index - kCDataLength;
+    return true;
+  }
+  if ((i + start_string_index) == string_length) {
+    // This branch is hit in the case of matching all available data, but
+    // more is required for a full match.
+    num_matching_cdata_chars_ += i;
+    return true;
+  }
+  return false;
+}
+
+void SpecialSequencesTracker::UpdateIndexOfCDATA(const String& string) {
+  if (num_matching_cdata_chars_ != 0) {
+    if (MatchPossibleCDataSection(string, 0))
+      return;
+    num_matching_cdata_chars_ = 0;
+  }
+
+  for (wtf_size_t next_possible_index = 0; next_possible_index != kNotFound;
+       next_possible_index = string.find(kCData[0], next_possible_index)) {
+    num_matching_cdata_chars_ = 1;
+    if (MatchPossibleCDataSection(string, next_possible_index + 1))
+      return;
+    ++next_possible_index;
+  }
+  num_matching_cdata_chars_ = 0;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/special_sequences_tracker.h b/third_party/blink/renderer/core/html/parser/special_sequences_tracker.h
new file mode 100644
index 0000000..a5ca546
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/special_sequences_tracker.h
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_SPECIAL_SEQUENCES_TRACKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_SPECIAL_SEQUENCES_TRACKER_H_
+
+#include <limits>
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
+
+namespace WTF {
+class String;
+}
+
+namespace blink {
+
+// Used to track sequences that can not be handled the
+// BackgroundHTMLTokenProducer.
+class CORE_EXPORT SpecialSequencesTracker {
+ public:
+  static constexpr unsigned kNoSpecialSequencesFound =
+      std::numeric_limits<unsigned>::max();
+
+  void UpdateIndices(const WTF::String& string);
+
+  // Returns the index of the first sequence that can't be handled. Returns
+  // the max unsized value if no special sequences have been encountered.
+  unsigned index_of_first_special_sequence() const {
+    return special_sequence_index_;
+  }
+
+ private:
+  unsigned IndexOfNullChar(const WTF::String& string) const;
+
+  // Returns true on success, or the section matches but the end of input is
+  // reached (partial match).
+  bool MatchPossibleCDataSection(const WTF::String& string,
+                                 wtf_size_t start_string_index);
+
+  void UpdateIndexOfCDATA(const WTF::String& string);
+
+  // Length of all strings supplied to UpdateIndices() (until a special sequence
+  // has been encountered).
+  unsigned total_string_length_ = 0;
+
+  // Index of the special sequence.
+  unsigned special_sequence_index_ = std::numeric_limits<unsigned>::max();
+
+  // If non-zero a partial match of CDATA has been encountered.
+  wtf_size_t num_matching_cdata_chars_ = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_SPECIAL_SEQUENCES_TRACKER_H_
diff --git a/third_party/blink/renderer/core/html/parser/special_sequences_tracker_test.cc b/third_party/blink/renderer/core/html/parser/special_sequences_tracker_test.cc
new file mode 100644
index 0000000..aca1345
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/special_sequences_tracker_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/parser/special_sequences_tracker.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(SpecialSequencesTrackerTest, FindNullChar) {
+  SpecialSequencesTracker tracker;
+  tracker.UpdateIndices(String("123\0", 4u));
+  EXPECT_EQ(3u, tracker.index_of_first_special_sequence());
+}
+
+TEST(SpecialSequencesTrackerTest, FindNullCharSecondChunk) {
+  SpecialSequencesTracker tracker;
+  tracker.UpdateIndices("abc");
+  tracker.UpdateIndices(String("d\0f", 3u));
+  EXPECT_EQ(4u, tracker.index_of_first_special_sequence());
+}
+
+TEST(SpecialSequencesTrackerTest, SimpleCData) {
+  SpecialSequencesTracker tracker;
+  tracker.UpdateIndices("abcdef<<![CDATA[");
+  EXPECT_EQ(7u, tracker.index_of_first_special_sequence());
+}
+
+TEST(SpecialSequencesTrackerTest, SplitCData) {
+  SpecialSequencesTracker tracker;
+  tracker.UpdateIndices("abc<![");
+  tracker.UpdateIndices("CD");
+  tracker.UpdateIndices("ATA[");
+  EXPECT_EQ(3u, tracker.index_of_first_special_sequence());
+}
+
+TEST(SpecialSequencesTrackerTest, IncompleteCData) {
+  SpecialSequencesTracker tracker;
+  tracker.UpdateIndices("abcdef<<![CDATA");
+  EXPECT_EQ(SpecialSequencesTracker::kNoSpecialSequencesFound,
+            tracker.index_of_first_special_sequence());
+}
+
+TEST(SpecialSequencesTrackerTest, SplitWithPartialThenFull) {
+  SpecialSequencesTracker tracker;
+  tracker.UpdateIndices("abc<![");
+  tracker.UpdateIndices("<![CDATA[");
+  EXPECT_EQ(6u, tracker.index_of_first_special_sequence());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/text_document_parser.cc b/third_party/blink/renderer/core/html/parser/text_document_parser.cc
index 1cfe08f..4547c7c6 100644
--- a/third_party/blink/renderer/core/html/parser/text_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/text_document_parser.cc
@@ -32,7 +32,10 @@
 
 TextDocumentParser::TextDocumentParser(HTMLDocument& document,
                                        ParserSynchronizationPolicy sync_policy)
-    : HTMLDocumentParser(document, sync_policy, kDisallowPrefetching),
+    : HTMLDocumentParser(document,
+                         sync_policy,
+                         kDisallowPrefetching,
+                         /* can_use_background_token_producer= */ false),
       have_inserted_fake_pre_element_(false) {}
 
 TextDocumentParser::~TextDocumentParser() = default;
diff --git a/third_party/blink/renderer/core/html/rel_list.cc b/third_party/blink/renderer/core/html/rel_list.cc
index c589ecb..d0905a6 100644
--- a/third_party/blink/renderer/core/html/rel_list.cc
+++ b/third_party/blink/renderer/core/html/rel_list.cc
@@ -35,6 +35,8 @@
                           "apple-touch-icon",
                           "apple-touch-icon-precomposed",
                           "canonical",
+                          "modulepreload",
+                          "allowed-alt-sxg",
                       }));
 
   return tokens;
@@ -55,13 +57,7 @@
                                  ExceptionState&) const {
   //  https://html.spec.whatwg.org/C/#linkTypes
   if (GetElement().HasTagName(html_names::kLinkTag)) {
-    if (SupportedTokensLink().Contains(token_value) ||
-        token_value == "modulepreload") {
-      return true;
-    }
-    if (RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled(
-            GetElement().GetExecutionContext()) &&
-        token_value == "allowed-alt-sxg") {
+    if (SupportedTokensLink().Contains(token_value)) {
       return true;
     }
   } else if ((GetElement().HasTagName(html_names::kATag) ||
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 68924ff8..f2092a3 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -2242,8 +2242,7 @@
   // not within the element. Intersect the clipped rect with the first
   // outline rect to ensure that the selection rect only includes visible
   // points within the focused element.
-  Vector<gfx::Rect> outline_rects =
-      focused_element->OutlineRectsInVisualViewport();
+  Vector<gfx::Rect> outline_rects = focused_element->OutlineRectsInWidget();
   if (outline_rects.size() > 1)
     clipped_rect.Intersect(outline_rects[0]);
   return clipped_rect;
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 58892780..7895765 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -1191,7 +1191,7 @@
 
   Element* element = GetDocument().getElementById("b1");
   EXPECT_EQ("my tooltip 1", LastToolTipText());
-  EXPECT_EQ(element->BoundsInViewport(), LastToolTipBounds());
+  EXPECT_EQ(element->BoundsInWidget(), LastToolTipBounds());
 
   // Doing the same but for a button that doesn't have a tooltip text should
   // still trigger a tooltip update. The browser-side TooltipController will
@@ -1199,7 +1199,7 @@
   GetDocument().GetFrame()->GetEventHandler().KeyEvent(e);
   element = GetDocument().getElementById("b2");
   EXPECT_TRUE(LastToolTipText().IsNull());
-  EXPECT_EQ(element->BoundsInViewport(), LastToolTipBounds());
+  EXPECT_EQ(element->BoundsInWidget(), LastToolTipBounds());
 }
 
 // macOS doesn't have keyboard-triggered tooltips.
@@ -1228,7 +1228,7 @@
 
   Element* element = GetDocument().getElementById("b");
   EXPECT_EQ("my tooltip", LastToolTipText());
-  EXPECT_EQ(element->BoundsInViewport(), LastToolTipBounds());
+  EXPECT_EQ(element->BoundsInWidget(), LastToolTipBounds());
 }
 
 // macOS doesn't have keyboard-triggered tooltips.
@@ -1252,7 +1252,7 @@
 
   Element* element = GetDocument().getElementById("b");
   gfx::PointF mouse_press_point =
-      gfx::PointF(element->BoundsInViewport().CenterPoint());
+      gfx::PointF(element->BoundsInWidget().CenterPoint());
   WebMouseEvent mouse_press_event(
       WebInputEvent::Type::kMouseDown, mouse_press_point, mouse_press_point,
       WebPointerProperties::Button::kLeft, 1,
@@ -1336,7 +1336,7 @@
 
   Element* element = GetDocument().getElementById("b1");
   EXPECT_EQ("my tooltip 1", LastToolTipText());
-  EXPECT_EQ(element->BoundsInViewport(), LastToolTipBounds());
+  EXPECT_EQ(element->BoundsInWidget(), LastToolTipBounds());
 
   // Doing the same but for a button that doesn't have a tooltip text should
   // still trigger a tooltip update. The browser-side TooltipController will
@@ -1388,7 +1388,7 @@
 
   Element* element = GetDocument().getElementById("b1");
   EXPECT_EQ("my tooltip 1", LastToolTipText());
-  EXPECT_EQ(element->BoundsInViewport(), LastToolTipBounds());
+  EXPECT_EQ(element->BoundsInWidget(), LastToolTipBounds());
 
   // Validate that blurring an element that is not focused will not just hide
   // the tooltip. It wouldn't make sense.
@@ -1396,7 +1396,7 @@
   element->blur();
 
   EXPECT_EQ("my tooltip 1", LastToolTipText());
-  EXPECT_EQ(GetDocument().getElementById("b1")->BoundsInViewport(),
+  EXPECT_EQ(GetDocument().getElementById("b1")->BoundsInWidget(),
             LastToolTipBounds());
 
   // Then, programmatically move the focus to another button that has no title
@@ -1412,7 +1412,7 @@
 
   element = GetDocument().getElementById("b1");
   EXPECT_EQ("my tooltip 1", LastToolTipText());
-  EXPECT_EQ(element->BoundsInViewport(), LastToolTipBounds());
+  EXPECT_EQ(element->BoundsInWidget(), LastToolTipBounds());
 
   // Then, programmatically blur the button to validate that the tooltip gets
   // hidden.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_frame_set_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_frame_set_layout_algorithm.cc
index 149bb784..989d48a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_frame_set_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_frame_set_layout_algorithm.cc
@@ -23,6 +23,17 @@
   return LayoutUnit(floor(value));
 }
 
+// Adjusts proportionally the size with remaining size.
+LayoutUnit AdjustSizeToRemainingSize(LayoutUnit current,
+                                     LayoutUnit remaining,
+                                     int64_t total) {
+  // Performs the math operations step by step to avoid the overflow.
+  base::CheckedNumeric<int64_t> temp_product = current.ToInt();
+  temp_product *= remaining.ToInt();
+  temp_product /= total;
+  return LayoutUnit(base::checked_cast<int>(temp_product.ValueOrDie()));
+}
+
 }  // namespace
 
 NGFrameSetLayoutAlgorithm::NGFrameSetLayoutAlgorithm(
@@ -76,45 +87,100 @@
     return sizes;
   }
 
-  [[maybe_unused]] int64_t total_relative = 0;
-  [[maybe_unused]] int64_t total_fixed = 0;
-  [[maybe_unused]] int64_t total_percent = 0;
-  [[maybe_unused]] wtf_size_t count_relative = 0;
-  [[maybe_unused]] wtf_size_t count_fixed = 0;
-  [[maybe_unused]] wtf_size_t count_percent = 0;
+  // First we need to investigate how many columns of each type we have and
+  // how much space these columns are going to require.
+
+  Vector<wtf_size_t, 4> fixed_indices;
+  Vector<wtf_size_t, 4> percent_indices;
+  Vector<wtf_size_t, 4> relative_indices;
+  for (wtf_size_t i = 0; i < count; ++i) {
+    if (grid[i].IsAbsolute())
+      fixed_indices.push_back(i);
+    else if (grid[i].IsPercentage())
+      percent_indices.push_back(i);
+    else if (grid[i].IsRelative())
+      relative_indices.push_back(i);
+  }
+
+  int64_t total_relative = 0;
+  int64_t total_fixed = 0;
+  int64_t total_percent = 0;
 
   const float effective_zoom = Node().Style().EffectiveZoom();
 
-  // First we need to investigate how many columns of each type we have and
-  // how much space these columns are going to require.
-  for (wtf_size_t i = 0; i < count; ++i) {
-    // Count the total length of all of the fixed columns/rows -> total_fixed.
-    // Count the number of columns/rows which are fixed -> count_fixed.
-    if (grid[i].IsAbsolute()) {
+  // Count the total length of all of the fixed columns/rows.
+  for (auto i : fixed_indices) {
+    sizes[i] =
+        IntLayoutUnit(grid[i].Value() * effective_zoom).ClampNegativeToZero();
+    DCHECK(IsIntegerValue(sizes[i]));
+    total_fixed += sizes[i].ToInt();
+  }
+
+  // Count the total percentage of all of the percentage columns/rows.
+  for (auto i : percent_indices) {
+    sizes[i] = IntLayoutUnit(grid[i].Value() * available_length / 100.0)
+                   .ClampNegativeToZero();
+    DCHECK(IsIntegerValue(sizes[i])) << sizes[i];
+    total_percent += sizes[i].ToInt();
+  }
+
+  // Count the total relative of all the relative columns/rows.
+  for (auto i : relative_indices)
+    total_relative += ClampTo<int>(std::max(grid[i].Value(), 1.0));
+
+  LayoutUnit remaining_length = available_length;
+
+  // Fixed columns/rows are our first priority. If there is not enough space to
+  // fit all fixed columns/rows we need to proportionally adjust their size.
+  if (total_fixed > remaining_length.ToInt()) {
+    LayoutUnit remaining_fixed = remaining_length;
+    for (auto i : fixed_indices) {
       sizes[i] =
-          IntLayoutUnit(grid[i].Value() * effective_zoom).ClampNegativeToZero();
-      DCHECK(IsIntegerValue(sizes[i]));
-      total_fixed += sizes[i].ToInt();
-      ++count_fixed;
+          AdjustSizeToRemainingSize(sizes[i], remaining_fixed, total_fixed);
+      remaining_length -= sizes[i];
+    }
+  } else {
+    remaining_length -= total_fixed;
+  }
+
+  // Percentage columns/rows are our second priority. Divide the remaining space
+  // proportionally over all percentage columns/rows.
+  // NOTE: the size of each column/row is not relative to 100%, but to the total
+  // percentage. For example, if there are three columns, each of 75%, and the
+  // available space is 300px, each column will become 100px in width.
+  if (total_percent > remaining_length.ToInt()) {
+    LayoutUnit remaining_percent = remaining_length;
+    for (auto i : percent_indices) {
+      sizes[i] =
+          AdjustSizeToRemainingSize(sizes[i], remaining_percent, total_percent);
+      remaining_length -= sizes[i];
+    }
+  } else {
+    remaining_length -= total_percent;
+  }
+
+  // Relative columns/rows are our last priority. Divide the remaining space
+  // proportionally over all relative columns/rows.
+  // NOTE: the relative value of 0* is treated as 1*.
+  if (!relative_indices.IsEmpty()) {
+    wtf_size_t last_relative_index = WTF::kNotFound;
+    int64_t remaining_relative = remaining_length.ToInt();
+    for (auto i : relative_indices) {
+      sizes[i] = IntLayoutUnit(
+          (ClampTo<int>(std::max(grid[i].Value(), 1.)) * remaining_relative) /
+          total_relative);
+      remaining_length -= sizes[i];
+      last_relative_index = i;
     }
 
-    // Count the total percentage of all of the percentage columns/rows ->
-    // total_percent. Count the number of columns/rows which are percentages ->
-    // count_percent.
-    if (grid[i].IsPercentage()) {
-      sizes[i] = IntLayoutUnit(grid[i].Value() * available_length / 100.0)
-                     .ClampNegativeToZero();
-      DCHECK(IsIntegerValue(sizes[i])) << sizes[i];
-      total_percent += sizes[i].ToInt();
-      ++count_percent;
-    }
-
-    // Count the total relative of all the relative columns/rows ->
-    // total_relative. Count the number of columns/rows which are relative ->
-    // count_relative.
-    if (grid[i].IsRelative()) {
-      total_relative += ClampTo<int>(std::max(grid[i].Value(), 1.0));
-      ++count_relative;
+    // If we could not evenly distribute the available space of all of the
+    // relative columns/rows, the remainder will be added to the last column/
+    // row. For example: if we have a space of 100px and three columns (*,*,*),
+    // the remainder will be 1px and will be added to the last column: 33px,
+    // 33px, 34px.
+    if (remaining_length) {
+      sizes[last_relative_index] += remaining_length;
+      remaining_length = LayoutUnit();
     }
   }
 
diff --git a/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h b/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h
index 7274e9f1..705385b0 100644
--- a/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h
+++ b/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h
@@ -50,9 +50,6 @@
 // Note: When a valid "allowed-alt-sxg" link header exists in the inner response
 // but there is no matching "alternate" link header in the outer response, this
 // class keep the information with an invalid |alternative_url|.
-//
-// AlternateSignedExchangeResourceInfo is used only when
-// SignedExchangeSubresourcePrefetch is enabled.
 class CORE_EXPORT AlternateSignedExchangeResourceInfo {
  public:
   class CORE_EXPORT Entry {
diff --git a/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc b/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc
index 2c4f2c5..616835a 100644
--- a/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc
+++ b/third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info_test.cc
@@ -6,16 +6,12 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
-#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
-class AlternateSignedExchangeResourceInfoTest
-    : public testing::Test,
-      private ScopedSignedExchangeSubresourcePrefetchForTest {
+class AlternateSignedExchangeResourceInfoTest : public testing::Test {
  public:
-  AlternateSignedExchangeResourceInfoTest()
-      : ScopedSignedExchangeSubresourcePrefetchForTest(true) {}
+  AlternateSignedExchangeResourceInfoTest() = default;
   AlternateSignedExchangeResourceInfoTest(
       const AlternateSignedExchangeResourceInfoTest&) = delete;
   AlternateSignedExchangeResourceInfoTest& operator=(
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index d8784cd..a458f83 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -499,15 +499,15 @@
       const WebVector<network::mojom::WebClientHintsType>&
           enabled_client_hints);
 
-  // For SignedExchangeSubresourcePrefetch feature. If the page was loaded from
-  // a signed exchage which has "allowed-alt-sxg" link headers in the inner
-  // response and PrefetchedSignedExchanges were passed from the previous page,
-  // initializes a PrefetchedSignedExchangeManager which will hold the
-  // subresource signed exchange related headers ("alternate" link header in the
-  // outer response and "allowed-alt-sxg" link header in the inner response of
-  // the page's signed exchange), and the passed PrefetchedSignedExchanges.
-  // The created PrefetchedSignedExchangeManager will be used to load the
-  // prefetched signed exchanges for matching requests.
+  // If the page was loaded from a signed exchange which has "allowed-alt-sxg"
+  // link headers in the inner response and PrefetchedSignedExchanges were
+  // passed from the previous page, initializes a
+  // PrefetchedSignedExchangeManager which will hold the subresource signed
+  // exchange related headers ("alternate" link header in the outer response and
+  // "allowed-alt-sxg" link header in the inner response of the page's signed
+  // exchange), and the passed PrefetchedSignedExchanges. The created
+  // PrefetchedSignedExchangeManager will be used to load the prefetched signed
+  // exchanges for matching requests.
   void InitializePrefetchedSignedExchangeManager();
 
   bool IsJavaScriptURLOrXSLTCommit() const {
diff --git a/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h b/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
index f623309..8fc4a051 100644
--- a/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
+++ b/third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
@@ -22,9 +22,8 @@
 class WebURLLoader;
 class WebURLRequest;
 
-// For SignedExchangeSubresourcePrefetch feature. This class holds the
-// prefetched signed exchange info and will returns loaders for matching
-// requests.
+// This class holds the prefetched signed exchange info and will returns
+// loaders for matching requests.
 class PrefetchedSignedExchangeManager final
     : public GarbageCollected<PrefetchedSignedExchangeManager> {
  public:
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index 4bc28ae..ae4ae91f 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -622,9 +622,6 @@
         document.GetExecutionContext()->GetSecurityOrigin(),
         params.cross_origin);
   }
-  link_fetch_params.SetSignedExchangePrefetchCacheEnabled(
-      RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled(
-          document.GetExecutionContext()));
   Resource* resource =
       LinkPrefetchResource::Fetch(link_fetch_params, document.Fetcher());
   if (pending_preload)
@@ -667,8 +664,6 @@
 
     if (alternate_resource_info && params.rel.IsLinkPreload()) {
       DCHECK(document);
-      DCHECK(RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled(
-          document->GetExecutionContext()));
       KURL url = params.href;
       absl::optional<ResourceType> resource_type =
           PreloadHelper::GetResourceTypeFromAsAttribute(params.as);
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
index 31218845..a7db7e1 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
@@ -280,9 +280,7 @@
       resource->GetType() == ResourceType::kLinkPrefetch) {
     CountUsage(WebFeature::kLinkRelPrefetchForSignedExchanges);
 
-    if (RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled(
-            document_->GetExecutionContext()) &&
-        resource->RedirectChainSize() > 0) {
+    if (resource->RedirectChainSize() > 0) {
       // See if the outer response (which must be the last response in
       // the redirect chain) had provided alternate links for the prefetch.
       alternate_resource_info =
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
index 6b7291c..c2803452 100644
--- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
+++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h"
 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
@@ -435,7 +436,9 @@
   // it's not useful to generate mobile friendliness metrics for
   // devtools, svg, etc.
   if (!frame_view.GetFrame().Client()->IsLocalFrameClientImpl() ||
-      !frame_view.GetFrame().IsOutermostMainFrame()) {
+      !frame_view.GetFrame().IsOutermostMainFrame() ||
+      !frame_view.GetPage()->GetSettings().GetViewportEnabled() ||
+      !frame_view.GetPage()->GetSettings().GetViewportMetaEnabled()) {
     return nullptr;
   }
   return MakeGarbageCollected<MobileFriendlinessChecker>(frame_view);
diff --git a/third_party/blink/renderer/core/page/chrome_client.cc b/third_party/blink/renderer/core/page/chrome_client.cc
index 07df0300..8cf1b06 100644
--- a/third_party/blink/renderer/core/page/chrome_client.cc
+++ b/third_party/blink/renderer/core/page/chrome_client.cc
@@ -226,7 +226,7 @@
   if (layout_object) {
     TextDirection tooltip_direction = layout_object->StyleRef().Direction();
     UpdateTooltipFromKeyboard(frame, tooltip_text, tooltip_direction,
-                              element->BoundsInViewport());
+                              element->BoundsInWidget());
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.cc
index eff97b5..78fbd3ae 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.cc
@@ -4,11 +4,28 @@
 
 #include "third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.h"
 
+#include "third_party/blink/renderer/core/layout/ng/frame_set_layout_data.h"
+#include "third_party/blink/renderer/core/paint/box_painter.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h"
+#include "third_party/blink/renderer/core/paint/paint_auto_dark_mode.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 
 namespace blink {
 
+namespace {
+
+constexpr Color kBorderStartEdgeColor = Color::FromRGB(170, 170, 170);
+constexpr Color kBorderEndEdgeColor = Color::FromRGB(0, 0, 0);
+constexpr Color kBorderFillColor = Color::FromRGB(208, 208, 208);
+
+bool ShouldPaintBorderAfter(const Vector<bool>& allow_border,
+                            wtf_size_t index) {
+  // Should not paint a border after the last frame along the axis.
+  return index + 1 < allow_border.size() - 1 && allow_border[index + 1];
+}
+
+}  // namespace
+
 void NGFrameSetPainter::PaintObject(const PaintInfo& paint_info,
                                     const PhysicalOffset& paint_offset) {
   if (paint_info.phase != PaintPhase::kForeground)
@@ -45,6 +62,104 @@
 }
 
 void NGFrameSetPainter::PaintBorders(const PaintInfo& paint_info,
-                                     const PhysicalOffset& paint_offset) {}
+                                     const PhysicalOffset& paint_offset) {
+  if (DrawingRecorder::UseCachedDrawingIfPossible(
+          paint_info.context, display_item_client_, paint_info.phase))
+    return;
+
+  DrawingRecorder recorder(
+      paint_info.context, display_item_client_, paint_info.phase,
+      BoxPainter(*To<LayoutBox>(box_fragment_.GetLayoutObject()))
+          .VisualRect(paint_offset));
+
+  const FrameSetLayoutData* layout_data = box_fragment_.GetFrameSetLayoutData();
+  const LayoutUnit border_thickness = LayoutUnit(layout_data->border_thickness);
+  if (border_thickness <= 0)
+    return;
+
+  const ComputedStyle& style = box_fragment_.Style();
+  Color border_fill_color =
+      layout_data->has_border_color
+          ? style.VisitedDependentColor(GetCSSPropertyBorderLeftColor())
+          : kBorderFillColor;
+  auto auto_dark_mode = BorderPaintAutoDarkMode(style, border_fill_color);
+
+  size_t children_count = box_fragment_.Children().size();
+  const Vector<LayoutUnit>& row_sizes = layout_data->row_sizes;
+  const Vector<LayoutUnit>& col_sizes = layout_data->col_sizes;
+  LayoutUnit y;
+  for (wtf_size_t row = 0; row < row_sizes.size(); ++row) {
+    LayoutUnit x;
+    for (wtf_size_t col = 0; col < col_sizes.size(); ++col) {
+      x += col_sizes[col];
+      if (ShouldPaintBorderAfter(layout_data->col_allow_border, col)) {
+        gfx::Rect rect = ToPixelSnappedRect(
+            PhysicalRect(paint_offset.left + x, paint_offset.top + y,
+                         border_thickness, box_fragment_.Size().height - y));
+        PaintColumnBorder(paint_info, rect, border_fill_color, auto_dark_mode);
+        x += border_thickness;
+      }
+      if (--children_count == 0)
+        return;
+    }
+    y += row_sizes[row];
+    if (ShouldPaintBorderAfter(layout_data->row_allow_border, row)) {
+      gfx::Rect rect = ToPixelSnappedRect(
+          PhysicalRect(paint_offset.left, paint_offset.top + y,
+                       box_fragment_.Size().width, border_thickness));
+      PaintRowBorder(paint_info, rect, border_fill_color, auto_dark_mode);
+      y += border_thickness;
+    }
+  }
+}
+
+void NGFrameSetPainter::PaintRowBorder(const PaintInfo& paint_info,
+                                       const gfx::Rect& border_rect,
+                                       const Color& fill_color,
+                                       const AutoDarkMode& auto_dark_mode) {
+  // Fill first.
+  GraphicsContext& context = paint_info.context;
+  context.FillRect(border_rect, fill_color, auto_dark_mode);
+
+  // Now stroke the edges but only if we have enough room to paint both edges
+  // with a little bit of the fill color showing through.
+  if (border_rect.height() < 3)
+    return;
+  const ComputedStyle& style = box_fragment_.Style();
+  context.FillRect(
+      gfx::Rect(border_rect.origin(), gfx::Size(border_rect.width(), 1)),
+      kBorderStartEdgeColor,
+      BorderPaintAutoDarkMode(style, kBorderStartEdgeColor));
+  context.FillRect(gfx::Rect(border_rect.x(), border_rect.bottom() - 1,
+                             border_rect.width(), 1),
+                   kBorderEndEdgeColor,
+                   BorderPaintAutoDarkMode(style, kBorderEndEdgeColor));
+}
+
+void NGFrameSetPainter::PaintColumnBorder(const PaintInfo& paint_info,
+                                          const gfx::Rect& border_rect,
+                                          const Color& fill_color,
+                                          const AutoDarkMode& auto_dark_mode) {
+  if (!paint_info.GetCullRect().Intersects(border_rect))
+    return;
+
+  // Fill first.
+  GraphicsContext& context = paint_info.context;
+  context.FillRect(border_rect, fill_color, auto_dark_mode);
+
+  // Now stroke the edges but only if we have enough room to paint both edges
+  // with a little bit of the fill color showing through.
+  if (border_rect.width() < 3)
+    return;
+  const ComputedStyle& style = box_fragment_.Style();
+  context.FillRect(
+      gfx::Rect(border_rect.origin(), gfx::Size(1, border_rect.height())),
+      kBorderStartEdgeColor,
+      BorderPaintAutoDarkMode(style, kBorderStartEdgeColor));
+  context.FillRect(gfx::Rect(border_rect.right() - 1, border_rect.y(), 1,
+                             border_rect.height()),
+                   kBorderEndEdgeColor,
+                   BorderPaintAutoDarkMode(style, kBorderEndEdgeColor));
+}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.h b/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.h
index dbd007e..f155781f 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_frame_set_painter.h
@@ -7,10 +7,16 @@
 
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
+namespace gfx {
+class Rect;
+}
+
 namespace blink {
 
+class Color;
 class DisplayItemClient;
 class NGPhysicalBoxFragment;
+struct AutoDarkMode;
 struct PaintInfo;
 struct PhysicalOffset;
 
@@ -28,9 +34,17 @@
   void PaintChildren(const PaintInfo& paint_info);
   void PaintBorders(const PaintInfo& paint_info,
                     const PhysicalOffset& paint_offset);
+  void PaintRowBorder(const PaintInfo& paint_info,
+                      const gfx::Rect& border_rect,
+                      const Color& fill_color,
+                      const AutoDarkMode& auto_dark_mode);
+  void PaintColumnBorder(const PaintInfo& paint_info,
+                         const gfx::Rect& border_rect,
+                         const Color& fill_color,
+                         const AutoDarkMode& auto_dark_mode);
 
   const NGPhysicalBoxFragment& box_fragment_;
-  [[maybe_unused]] const DisplayItemClient& display_item_client_;
+  const DisplayItemClient& display_item_client_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc
index 5a2f0eb..34c774e0 100644
--- a/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.cc
@@ -96,8 +96,7 @@
 
 std::unique_ptr<webrtc::RtpParameters> FakeRTCRtpSenderImpl::GetParameters()
     const {
-  NOTIMPLEMENTED();
-  return nullptr;
+  return std::make_unique<webrtc::RtpParameters>();
 }
 
 void FakeRTCRtpSenderImpl::SetParameters(
@@ -204,11 +203,6 @@
 
 FakeRTCRtpTransceiverImpl::~FakeRTCRtpTransceiverImpl() {}
 
-RTCRtpTransceiverPlatformImplementationType
-FakeRTCRtpTransceiverImpl::ImplementationType() const {
-  return RTCRtpTransceiverPlatformImplementationType::kFullTransceiver;
-}
-
 uintptr_t FakeRTCRtpTransceiverImpl::Id() const {
   NOTIMPLEMENTED();
   return 0u;
diff --git a/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h b/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h
index e19e596..72350667 100644
--- a/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/fake_rtc_rtp_transceiver_impl.h
@@ -96,8 +96,6 @@
       absl::optional<webrtc::RtpTransceiverDirection> current_direction);
   ~FakeRTCRtpTransceiverImpl() override;
 
-  RTCRtpTransceiverPlatformImplementationType ImplementationType()
-      const override;
   uintptr_t Id() const override;
   String Mid() const override;
   std::unique_ptr<blink::RTCRtpSenderPlatform> Sender() const override;
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc
index e1cefc7..ef4e94b 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc
@@ -384,6 +384,18 @@
   rtc::scoped_refptr<FakeRtpSender> sender(
       new rtc::RefCountedObject<FakeRtpSender>(track, stream_ids));
   senders_.push_back(sender);
+  // This mock is dumb. It creates an audio transceiver without checking the
+  // kind of the sender track.
+  rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> dummy_receiver_track(
+      blink::MockWebRtcAudioTrack::Create("dummy_track").get());
+  rtc::scoped_refptr<FakeRtpReceiver> dummy_receiver(
+      new rtc::RefCountedObject<FakeRtpReceiver>(dummy_receiver_track));
+  rtc::scoped_refptr<FakeRtpTransceiver> transceiver(
+      new rtc::RefCountedObject<FakeRtpTransceiver>(
+          cricket::MediaType::MEDIA_TYPE_AUDIO, sender, dummy_receiver,
+          absl::nullopt, false, webrtc::RtpTransceiverDirection::kSendRecv,
+          absl::nullopt));
+  transceivers_.push_back(transceiver);
   return rtc::scoped_refptr<webrtc::RtpSenderInterface>(sender);
 }
 
@@ -396,6 +408,8 @@
     return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
                             "Mock: sender not found in senders");
   }
+  // TODO(https://crbug.com/1302249): This is old Plan B behavior, don't remove
+  // the sender.
   senders_.erase(it);
   auto track = sender->track();
 
@@ -432,6 +446,14 @@
   return receivers;
 }
 
+std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
+MockPeerConnectionImpl::GetTransceivers() const {
+  std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
+  for (const auto& transceiver : transceivers_)
+    transceivers.push_back(transceiver);
+  return transceivers;
+}
+
 rtc::scoped_refptr<webrtc::DataChannelInterface>
 MockPeerConnectionImpl::CreateDataChannel(
     const std::string& label,
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.h b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.h
index 31d20c2..9f5e017 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.h
@@ -231,9 +231,7 @@
   std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> GetReceivers()
       const override;
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
-  GetTransceivers() const override {
-    return {};
-  }
+  GetTransceivers() const override;
   MOCK_CONST_METHOD0(GetSctpTransport,
                      rtc::scoped_refptr<webrtc::SctpTransportInterface>());
   rtc::scoped_refptr<webrtc::DataChannelInterface> CreateDataChannel(
@@ -416,6 +414,7 @@
   std::vector<std::string> local_stream_ids_;
   rtc::scoped_refptr<MockStreamCollection> remote_streams_;
   std::vector<rtc::scoped_refptr<FakeRtpSender>> senders_;
+  std::vector<rtc::scoped_refptr<FakeRtpTransceiver>> transceivers_;
   std::unique_ptr<webrtc::SessionDescriptionInterface> local_desc_;
   std::unique_ptr<webrtc::SessionDescriptionInterface> remote_desc_;
   std::unique_ptr<webrtc::SessionDescriptionInterface>
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.cc b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.cc
index 03d237f..3439f45 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.cc
@@ -16,9 +16,6 @@
       .WillByDefault(testing::Invoke(
           this,
           &MockRTCPeerConnectionHandlerClient::didGenerateICECandidateWorker));
-  ON_CALL(*this, DidModifyReceiversPlanBForMock(_, _, _))
-      .WillByDefault(testing::Invoke(
-          this, &MockRTCPeerConnectionHandlerClient::didModifyReceiversWorker));
 }
 
 MockRTCPeerConnectionHandlerClient::~MockRTCPeerConnectionHandlerClient() {}
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.h b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.h
index 0c7487e..403fd8f 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.h
+++ b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.h
@@ -47,19 +47,9 @@
                     RTCSessionDescriptionPlatform*));
   MOCK_METHOD1(DidChangeIceGatheringState,
                void(webrtc::PeerConnectionInterface::IceGatheringState state));
-  MOCK_METHOD1(DidChangeIceConnectionState,
-               void(webrtc::PeerConnectionInterface::IceConnectionState state));
   MOCK_METHOD1(
       DidChangePeerConnectionState,
       void(webrtc::PeerConnectionInterface::PeerConnectionState state));
-  void DidModifyReceiversPlanB(
-      webrtc::PeerConnectionInterface::SignalingState signaling_state,
-      Vector<std::unique_ptr<RTCRtpReceiverPlatform>> receivers_added,
-      Vector<std::unique_ptr<RTCRtpReceiverPlatform>> receivers_removed)
-      override {
-    DidModifyReceiversPlanBForMock(signaling_state, &receivers_added,
-                                   &receivers_removed);
-  }
   MOCK_METHOD1(DidModifySctpTransport,
                void(blink::WebRTCSctpTransportSnapshot snapshot));
   void DidModifyTransceivers(
@@ -77,10 +67,6 @@
 
   // Move-only arguments do not play nicely with MOCK, the workaround is to
   // EXPECT_CALL with these instead.
-  MOCK_METHOD3(DidModifyReceiversPlanBForMock,
-               void(webrtc::PeerConnectionInterface::SignalingState,
-                    Vector<std::unique_ptr<RTCRtpReceiverPlatform>>*,
-                    Vector<std::unique_ptr<RTCRtpReceiverPlatform>>*));
   MOCK_METHOD3(DidModifyTransceiversForMock,
                void(webrtc::PeerConnectionInterface::SignalingState,
                     Vector<std::unique_ptr<RTCRtpTransceiverPlatform>>*,
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc
index 05334d0..194c620a 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc
@@ -215,10 +215,6 @@
 
   scoped_refptr<DummyTransceiverInternal> internal() const { return internal_; }
 
-  RTCRtpTransceiverPlatformImplementationType ImplementationType()
-      const override {
-    return RTCRtpTransceiverPlatformImplementationType::kFullTransceiver;
-  }
   uintptr_t Id() const override { return internal_->id(); }
   String Mid() const override { return String(); }
   std::unique_ptr<RTCRtpSenderPlatform> Sender() const override {
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
index da0db583..88c5fc3d 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
@@ -193,6 +193,56 @@
   return direction ? SerializeDirection(*direction) : "null";
 }
 
+String SerializeEncodingParameters(
+    const String& indent,
+    const std::vector<webrtc::RtpEncodingParameters>& encodings) {
+  StringBuilder result;
+  if (encodings.empty()) {
+    return result.ToString();
+  }
+  result.Append(indent);
+  result.Append("encodings: [\n");
+  for (const auto& encoding : encodings) {
+    result.Append(indent);
+    result.Append("    {");
+    result.Append("active: ");
+    result.Append(encoding.active ? "true" : "false");
+    result.Append(", ");
+    if (encoding.max_bitrate_bps) {
+      result.Append("maxBitrate: ");
+      result.AppendNumber(*encoding.max_bitrate_bps);
+      result.Append(", ");
+    }
+    if (encoding.scale_resolution_down_by) {
+      result.Append("scaleResolutionDownBy: ");
+      result.AppendNumber(*encoding.scale_resolution_down_by);
+      result.Append(", ");
+    }
+    if (!encoding.rid.empty()) {
+      result.Append("rid: ");
+      result.Append(String(encoding.rid));
+      result.Append(", ");
+    }
+    if (encoding.max_framerate) {
+      result.Append("maxFramerate: ");
+      result.AppendNumber(*encoding.max_framerate);
+      result.Append(", ");
+    }
+    if (encoding.adaptive_ptime) {
+      result.Append("adaptivePtime: true, ");
+    }
+    if (encoding.scalability_mode) {
+      result.Append("scalabilityMode: ");
+      result.Append(String(*encoding.scalability_mode));
+    }
+    result.Append("},\n");
+  }
+  result.Append(indent);
+  result.Append("  ],\n");
+  result.Append(indent);
+  return result.ToString();
+}
+
 String SerializeSender(const String& indent,
                        const blink::RTCRtpSenderPlatform& sender) {
   StringBuilder result;
@@ -214,6 +264,8 @@
   result.Append(SerializeMediaStreamIds(sender.StreamIds()));
   result.Append(",\n");
   result.Append(indent);
+  result.Append(
+      SerializeEncodingParameters(indent, sender.GetParameters()->encodings));
   result.Append("}");
   return result.ToString();
 }
@@ -239,44 +291,34 @@
 }
 
 String SerializeTransceiver(const RTCRtpTransceiverPlatform& transceiver) {
-  if (transceiver.ImplementationType() ==
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
-    StringBuilder result;
-    result.Append("{\n");
-    // mid:'foo',
-    if (transceiver.Mid().IsNull()) {
-      result.Append("  mid:null,\n");
-    } else {
-      result.Append("  mid:'");
-      result.Append(String(transceiver.Mid()));
-      result.Append("',\n");
-    }
-    // sender:{...},
-    result.Append("  sender:");
-    result.Append(SerializeSender("  ", *transceiver.Sender()));
-    result.Append(",\n");
-    // receiver:{...},
-    result.Append("  receiver:");
-    result.Append(SerializeReceiver("  ", *transceiver.Receiver()));
-    result.Append(",\n");
-    // direction:'sendrecv',
-    result.Append("  direction:");
-    result.Append(SerializeDirection(transceiver.Direction()));
-    result.Append(",\n");
-    // currentDirection:null,
-    result.Append("  currentDirection:");
-    result.Append(SerializeOptionalDirection(transceiver.CurrentDirection()));
-    result.Append(",\n");
-    result.Append("}");
-    return result.ToString();
+  StringBuilder result;
+  result.Append("{\n");
+  // mid:'foo',
+  if (transceiver.Mid().IsNull()) {
+    result.Append("  mid:null,\n");
+  } else {
+    result.Append("  mid:'");
+    result.Append(String(transceiver.Mid()));
+    result.Append("',\n");
   }
-  if (transceiver.ImplementationType() ==
-      RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
-    return SerializeSender("", *transceiver.Sender());
-  }
-  DCHECK(transceiver.ImplementationType() ==
-         RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-  return SerializeReceiver("", *transceiver.Receiver());
+  // sender:{...},
+  result.Append("  sender:");
+  result.Append(SerializeSender("  ", *transceiver.Sender()));
+  result.Append(",\n");
+  // receiver:{...},
+  result.Append("  receiver:");
+  result.Append(SerializeReceiver("  ", *transceiver.Receiver()));
+  result.Append(",\n");
+  // direction:'sendrecv',
+  result.Append("  direction:");
+  result.Append(SerializeDirection(transceiver.Direction()));
+  result.Append(",\n");
+  // currentDirection:null,
+  result.Append("  currentDirection:");
+  result.Append(SerializeOptionalDirection(transceiver.CurrentDirection()));
+  result.Append(",\n");
+  result.Append("}");
+  return result.ToString();
 }
 
 String SerializeIceTransportType(
@@ -336,21 +378,6 @@
   return policy_str;
 }
 
-String SerializeSdpSemantics(webrtc::SdpSemantics sdp_semantics) {
-  String sdp_semantics_str("");
-  switch (sdp_semantics) {
-    case webrtc::SdpSemantics::kPlanB:
-      sdp_semantics_str = "plan-b";
-      break;
-    case webrtc::SdpSemantics::kUnifiedPlan:
-      sdp_semantics_str = "unified-plan";
-      break;
-    default:
-      NOTREACHED();
-  }
-  return "\"" + sdp_semantics_str + "\"";
-}
-
 // Serializes things that are of interest from the RTCConfiguration. Note that
 // this does not include some parameters that were passed down via
 // GoogMediaConstraints; see SerializePeerConnectionMediaConstraints() for that.
@@ -369,8 +396,6 @@
   result.Append(SerializeRtcpMuxPolicy(config.rtcp_mux_policy));
   result.Append(", iceCandidatePoolSize: ");
   result.AppendNumber(config.ice_candidate_pool_size);
-  result.Append(", sdpSemantics: ");
-  result.Append(SerializeSdpSemantics(config.sdp_semantics));
   if (usesInsertableStreams) {
     result.Append(", encodedInsertableStreams: true");
   }
@@ -636,7 +661,7 @@
       Supplement<LocalDOMWindow>::From<PeerConnectionTracker>(window);
   if (!tracker) {
     tracker = MakeGarbageCollected<PeerConnectionTracker>(
-        window, Thread::MainThread()->GetDeprecatedTaskRunner(),
+        window, window.GetTaskRunner(TaskType::kNetworking),
         base::PassKey<PeerConnectionTracker>());
     ProvideTo(window, tracker);
   }
@@ -949,15 +974,6 @@
                    transceiver_index);
 }
 
-void PeerConnectionTracker::TrackRemoveTransceiver(
-    RTCPeerConnectionHandler* pc_handler,
-    PeerConnectionTracker::TransceiverUpdatedReason reason,
-    const RTCRtpTransceiverPlatform& transceiver,
-    size_t transceiver_index) {
-  TrackTransceiver("Removed", pc_handler, reason, transceiver,
-                   transceiver_index);
-}
-
 void PeerConnectionTracker::TrackTransceiver(
     const char* callback_type_ending,
     RTCPeerConnectionHandler* pc_handler,
@@ -968,34 +984,15 @@
   int id = GetLocalIDForHandler(pc_handler);
   if (id == -1)
     return;
-  String callback_type;
-  if (transceiver.ImplementationType() ==
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
-    callback_type = "transceiver";
-  } else if (transceiver.ImplementationType() ==
-             RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
-    callback_type = "sender";
-  } else {
-    callback_type = "receiver";
-  }
-  callback_type = callback_type + callback_type_ending;
-
+  String callback_type = "transceiver" + String::FromUTF8(callback_type_ending);
   StringBuilder result;
   result.Append("Caused by: ");
   result.Append(GetTransceiverUpdatedReasonString(reason));
   result.Append("\n\n");
-  if (transceiver.ImplementationType() ==
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
-    result.Append("getTransceivers()");
-  } else if (transceiver.ImplementationType() ==
-             RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
-    result.Append("getSenders()");
-  } else {
-    DCHECK_EQ(transceiver.ImplementationType(),
-              RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-    result.Append("getReceivers()");
-  }
-  result.Append(String("[" + String::Number(transceiver_index) + "]:"));
+  result.Append("getTransceivers()");
+  result.Append("[");
+  result.Append(String::Number(transceiver_index));
+  result.Append("]:");
   result.Append(SerializeTransceiver(transceiver));
   SendPeerConnectionUpdate(id, callback_type, result.ToString());
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h
index 6525a9a0..620906c 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h
@@ -87,8 +87,6 @@
     kActionCreateAnswer
   };
 
-  // In Plan B: "Transceiver" refers to RTCRtpSender or RTCRtpReceiver.
-  // In Unified Plan: "Transceiver" refers to RTCRtpTransceiver.
   enum class TransceiverUpdatedReason {
     kAddTransceiver,
     kAddTrack,
@@ -153,9 +151,6 @@
 
   // Sends an update when a transceiver is added, modified or removed. This can
   // happen as a result of any of the methods indicated by |reason|.
-  // In Plan B: |transceiver| refers to its Sender() or Receiver() depending on
-  // ImplementationType(). Example events: "senderAdded", "receiverRemoved".
-  // In Plan B: |transceiver| has a fully implemented ImplementationType().
   // Example events: "transceiverAdded", "transceiverModified".
   // See peer_connection_tracker_unittest.cc for expected resulting event
   // strings.
@@ -168,13 +163,6 @@
       TransceiverUpdatedReason reason,
       const RTCRtpTransceiverPlatform& transceiver,
       size_t transceiver_index);
-  // TODO(hbos): When Plan B is removed this is no longer applicable.
-  // https://crbug.com/857004
-  virtual void TrackRemoveTransceiver(
-      RTCPeerConnectionHandler* pc_handler,
-      TransceiverUpdatedReason reason,
-      const RTCRtpTransceiverPlatform& transceiver,
-      size_t transceiver_index);
 
   // Sends an update when a DataChannel is created.
   virtual void TrackCreateDataChannel(
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker_test.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker_test.cc
index 40f6821..1e30d96a 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker_test.cc
@@ -42,18 +42,6 @@
     "  currentDirection:null,\n"
     "}";
 
-const char* kDefaultSenderString =
-    "getSenders()[0]:{\n"
-    "  track:'senderTrackId',\n"
-    "  streams:['senderStreamId'],\n"
-    "}";
-
-const char* kDefaultReceiverString =
-    "getReceivers()[0]:{\n"
-    "  track:'receiverTrackId',\n"
-    "  streams:['receiverStreamId'],\n"
-    "}";
-
 class MockPeerConnectionTrackerHost
     : public blink::mojom::blink::PeerConnectionTrackerHost {
  public:
@@ -85,14 +73,11 @@
 };
 
 // Creates a transceiver that is expected to be logged as
-// |kDefaultTransceiverString|, |kDefaultSenderString| or
-// |kDefaultReceiverString| depending on if |implementation_type| refers to a
-// fully implemented, sender-only or receiver-only transceiver.
+// |kDefaultTransceiverString|.
 //
 // This is used in unittests that don't care about the specific attributes of
 // the transceiver.
-std::unique_ptr<RTCRtpTransceiverPlatform> CreateDefaultTransceiver(
-    RTCRtpTransceiverPlatformImplementationType implementation_type) {
+std::unique_ptr<RTCRtpTransceiverPlatform> CreateDefaultTransceiver() {
   std::unique_ptr<RTCRtpTransceiverPlatform> transceiver;
   blink::FakeRTCRtpSenderImpl sender(
       "senderTrackId", {"senderStreamId"},
@@ -100,22 +85,10 @@
   blink::FakeRTCRtpReceiverImpl receiver(
       "receiverTrackId", {"receiverStreamId"},
       blink::scheduler::GetSingleThreadTaskRunnerForTesting());
-  if (implementation_type ==
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
-    transceiver = std::make_unique<blink::FakeRTCRtpTransceiverImpl>(
-        absl::nullopt, std::move(sender), std::move(receiver),
-        webrtc::RtpTransceiverDirection::kSendOnly /* direction */,
-        absl::nullopt /* current_direction */);
-  } else if (implementation_type ==
-             RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
-    transceiver = std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
-        std::make_unique<blink::FakeRTCRtpSenderImpl>(sender));
-  } else {
-    DCHECK_EQ(implementation_type,
-              RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-    transceiver = std::make_unique<blink::RTCRtpReceiverOnlyTransceiver>(
-        std::make_unique<blink::FakeRTCRtpReceiverImpl>(receiver));
-  }
+  transceiver = std::make_unique<blink::FakeRTCRtpTransceiverImpl>(
+      absl::nullopt, std::move(sender), std::move(receiver),
+      webrtc::RtpTransceiverDirection::kSendOnly /* direction */,
+      absl::nullopt /* current_direction */);
   return transceiver;
 }
 
@@ -368,8 +341,7 @@
 TEST_F(PeerConnectionTrackerTest, ModifyTransceiver) {
   CreateTrackerWithMocks();
   CreateAndRegisterPeerConnectionHandler();
-  auto transceiver = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver);
+  auto transceiver = CreateDefaultTransceiver();
   String update_value;
   EXPECT_CALL(*mock_host_,
               UpdatePeerConnection(_, String("transceiverModified"), _))
@@ -384,154 +356,10 @@
   EXPECT_EQ(expected_value, update_value);
 }
 
-TEST_F(PeerConnectionTrackerTest, RemoveTransceiver) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto transceiver = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver);
-  String update_value;
-  EXPECT_CALL(*mock_host_,
-              UpdatePeerConnection(_, String("transceiverRemoved"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackRemoveTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack,
-      *transceiver, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: removeTrack\n"
-      "\n" +
-      String(kDefaultTransceiverString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
-TEST_F(PeerConnectionTrackerTest, AddSender) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto sender_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly);
-  String update_value;
-  EXPECT_CALL(*mock_host_, UpdatePeerConnection(_, String("senderAdded"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackAddTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kSetLocalDescription,
-      *sender_only, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: setLocalDescription\n"
-      "\n" +
-      String(kDefaultSenderString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
-TEST_F(PeerConnectionTrackerTest, ModifySender) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto sender_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly);
-  String update_value;
-  EXPECT_CALL(*mock_host_, UpdatePeerConnection(_, String("senderModified"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackModifyTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kSetRemoteDescription,
-      *sender_only, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: setRemoteDescription\n"
-      "\n" +
-      String(kDefaultSenderString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
-TEST_F(PeerConnectionTrackerTest, RemoveSender) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto sender_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly);
-  String update_value;
-  EXPECT_CALL(*mock_host_, UpdatePeerConnection(_, String("senderRemoved"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackRemoveTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kSetRemoteDescription,
-      *sender_only, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: setRemoteDescription\n"
-      "\n" +
-      String(kDefaultSenderString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
-TEST_F(PeerConnectionTrackerTest, AddReceiver) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto receiver_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-  String update_value;
-  EXPECT_CALL(*mock_host_, UpdatePeerConnection(_, String("receiverAdded"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackAddTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kSetRemoteDescription,
-      *receiver_only, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: setRemoteDescription\n"
-      "\n" +
-      String(kDefaultReceiverString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
-TEST_F(PeerConnectionTrackerTest, ModifyReceiver) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto receiver_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-  String update_value;
-  EXPECT_CALL(*mock_host_,
-              UpdatePeerConnection(_, String("receiverModified"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackModifyTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kSetRemoteDescription,
-      *receiver_only, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: setRemoteDescription\n"
-      "\n" +
-      String(kDefaultReceiverString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
-TEST_F(PeerConnectionTrackerTest, RemoveReceiver) {
-  CreateTrackerWithMocks();
-  CreateAndRegisterPeerConnectionHandler();
-  auto receiver_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-  String update_value;
-  EXPECT_CALL(*mock_host_,
-              UpdatePeerConnection(_, String("receiverRemoved"), _))
-      .WillOnce(testing::SaveArg<2>(&update_value));
-  tracker_->TrackRemoveTransceiver(
-      mock_handler_.get(),
-      PeerConnectionTracker::TransceiverUpdatedReason::kSetRemoteDescription,
-      *receiver_only, 0u);
-  base::RunLoop().RunUntilIdle();
-  String expected_value(
-      "Caused by: setRemoteDescription\n"
-      "\n" +
-      String(kDefaultReceiverString));
-  EXPECT_EQ(expected_value, update_value);
-}
-
 TEST_F(PeerConnectionTrackerTest, IceCandidateError) {
   CreateTrackerWithMocks();
   CreateAndRegisterPeerConnectionHandler();
-  auto receiver_only = CreateDefaultTransceiver(
-      RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
+  auto transceiver = CreateDefaultTransceiver();
   String update_value;
   EXPECT_CALL(*mock_host_,
               UpdatePeerConnection(_, String("icecandidateerror"), _))
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_configuration.idl b/third_party/blink/renderer/modules/peerconnection/rtc_configuration.idl
index fbba82e..40f2ac3 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_configuration.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_configuration.idl
@@ -24,8 +24,11 @@
     "require"
 };
 
-// Nonstandard
-// For usage, see transition plan referenced from https://crbug.com/799030
+// The only supported format is "unified-plan". Exposed in
+// RTCPeerConnection.getConfiguration().sdpSemantics for backwards-compatibility
+// reasons. "plan-b" is only listed here as to avoid a type error when
+// attempting to construct an RTCPeerConnection with "plan-b", but it will be
+// ignored.
 enum SdpSemantics {
   "plan-b",
   "unified-plan"
@@ -46,8 +49,7 @@
     // TODO(foolip): DOMString peerIdentity;
     sequence<RTCCertificate> certificates;
     [EnforceRange] octet iceCandidatePoolSize = 0;
-    // Nonstandard, added for Unified Plan migration
-    [RuntimeEnabled=RTCUnifiedPlan] SdpSemantics sdpSemantics;
+    SdpSemantics sdpSemantics = "unified-plan";
     [RuntimeEnabled=RtcAudioJitterBufferMaxPackets] long rtcAudioJitterBufferMaxPackets;
     [RuntimeEnabled=RtcAudioJitterBufferMaxPackets] boolean rtcAudioJitterBufferFastAccelerate;
     [RuntimeEnabled=RtcAudioJitterBufferMaxPackets] long rtcAudioJitterBufferMinDelayMs;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 6da9e3f0..79426f9 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -236,29 +236,6 @@
       candidate->usernameFragment());
 }
 
-enum SdpSemanticRequested {
-  kSdpSemanticRequestedDefault,
-  kSdpSemanticRequestedPlanB,
-  kSdpSemanticRequestedUnifiedPlan,
-  kSdpSemanticRequestedMax
-};
-
-SdpSemanticRequested GetSdpSemanticRequested(
-    const blink::RTCConfiguration* configuration) {
-  if (!configuration->hasSdpSemantics()) {
-    return kSdpSemanticRequestedDefault;
-  }
-  if (configuration->sdpSemantics() == "plan-b") {
-    return kSdpSemanticRequestedPlanB;
-  }
-  if (configuration->sdpSemantics() == "unified-plan") {
-    return kSdpSemanticRequestedUnifiedPlan;
-  }
-
-  NOTREACHED();
-  return kSdpSemanticRequestedDefault;
-}
-
 webrtc::PeerConnectionInterface::IceTransportsType IceTransportPolicyFromString(
     const String& policy) {
   if (policy == "relay")
@@ -303,20 +280,6 @@
     DCHECK_EQ(configuration->rtcpMuxPolicy(), "require");
   }
 
-  // Unified Plan is used by default (RTCUnifiedPlanByDefault is
-  // ENABLED_BY_DEFAULT). For testing-only purposes the default can be set to
-  // Plan B instead using --disable-features=RTCUnifiedPlanByDefault.
-  DCHECK_EQ(web_configuration.sdp_semantics,
-            webrtc::SdpSemantics::kUnifiedPlan);
-  if (!base::FeatureList::IsEnabled(features::kRTCUnifiedPlanByDefault) &&
-      !RuntimeEnabledFeatures::RTCUnifiedPlanByDefaultEnabled()) {
-    web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB;
-  }
-  if (web_configuration.sdp_semantics == webrtc::SdpSemantics::kPlanB) {
-    Deprecation::CountDeprecation(
-        context, WebFeature::kRTCPeerConnectionSdpSemanticsPlanB);
-  }
-
   if (configuration->hasIceServers()) {
     WebVector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
     for (const RTCIceServer* ice_server : configuration->iceServers()) {
@@ -497,52 +460,8 @@
   return sdp.Find("stereo=1") != kNotFound;
 }
 
-enum class SdpFormat {
-  kSimple,
-  kComplexPlanB,
-  kComplexUnifiedPlan,
-};
-
-absl::optional<SdpFormat> DeduceSdpFormat(
-    const ParsedSessionDescription& parsed_sdp) {
-  auto* session_description = parsed_sdp.description();
-  if (!session_description)
-    return absl::nullopt;
-  size_t num_audio_mlines = 0u;
-  size_t num_video_mlines = 0u;
-  size_t num_audio_tracks = 0u;
-  size_t num_video_tracks = 0u;
-  for (const cricket::ContentInfo& content :
-       session_description->description()->contents()) {
-    cricket::MediaType media_type = content.media_description()->type();
-    size_t num_tracks = std::max(static_cast<size_t>(1u),
-                                 content.media_description()->streams().size());
-    if (media_type == cricket::MEDIA_TYPE_AUDIO) {
-      ++num_audio_mlines;
-      num_audio_tracks += num_tracks;
-    } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
-      ++num_video_mlines;
-      num_video_tracks += num_tracks;
-    }
-  }
-  if (num_audio_mlines <= 1u && num_audio_tracks <= 1u &&
-      num_video_mlines <= 1u && num_video_tracks <= 1u) {
-    return SdpFormat::kSimple;
-  }
-  if ((num_audio_mlines == 1u && num_audio_tracks > 1u) ||
-      (num_video_mlines == 1u && num_video_tracks > 1u)) {
-    return SdpFormat::kComplexPlanB;
-  }
-  DCHECK(num_audio_mlines > 1u || num_audio_tracks > 1u ||
-         num_video_mlines > 1u || num_video_tracks > 1u);
-  return SdpFormat::kComplexUnifiedPlan;
-}
-
 }  // namespace
 
-const char kOnlySupportedInUnifiedPlanMessage[] =
-    "This operation is only supported in 'unified-plan'.";
-
 RTCPeerConnection::EventWrapper::EventWrapper(Event* event,
                                               BoolFunction function)
     : event_(event), setup_function_(std::move(function)) {}
@@ -616,28 +535,11 @@
   }
 
   RTCPeerConnection* peer_connection = MakeGarbageCollected<RTCPeerConnection>(
-      context, std::move(configuration), rtc_configuration->hasSdpSemantics(),
+      context, std::move(configuration),
       rtc_configuration->encodedInsertableStreams(), media_constraints,
       exception_state);
   if (exception_state.HadException())
     return nullptr;
-
-  SdpSemanticRequested sdp_semantics_requested =
-      GetSdpSemanticRequested(rtc_configuration);
-  UMA_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticRequested",
-                            sdp_semantics_requested, kSdpSemanticRequestedMax);
-
-  if (sdp_semantics_requested == kSdpSemanticRequestedPlanB) {
-    UseCounter::Count(context,
-                      WebFeature::kRTCPeerConnectionConstructedWithPlanB);
-  } else {
-    // Unified Plan is the default.
-    DCHECK(sdp_semantics_requested == kSdpSemanticRequestedDefault ||
-           sdp_semantics_requested == kSdpSemanticRequestedUnifiedPlan);
-    UseCounter::Count(context,
-                      WebFeature::kRTCPeerConnectionConstructedWithUnifiedPlan);
-  }
-
   return peer_connection;
 }
 
@@ -652,7 +554,6 @@
 RTCPeerConnection::RTCPeerConnection(
     ExecutionContext* context,
     webrtc::PeerConnectionInterface::RTCConfiguration configuration,
-    bool sdp_semantics_specified,
     bool encoded_insertable_streams,
     GoogMediaConstraints* media_constraints,
     ExceptionState& exception_state)
@@ -670,8 +571,6 @@
       peer_handler_unregistered_(true),
       closed_(true),
       suppress_events_(true),
-      sdp_semantics_(configuration.sdp_semantics),
-      sdp_semantics_specified_(sdp_semantics_specified),
       encoded_insertable_streams_(encoded_insertable_streams) {
   LocalDOMWindow* window = To<LocalDOMWindow>(context);
 
@@ -918,76 +817,6 @@
   return nullptr;
 }
 
-absl::optional<ComplexSdpCategory> RTCPeerConnection::CheckForComplexSdp(
-    const ParsedSessionDescription& parsed_sdp) const {
-  absl::optional<SdpFormat> sdp_format = DeduceSdpFormat(parsed_sdp);
-  if (!sdp_format) {
-    return sdp_semantics_specified_
-               ? ComplexSdpCategory::kErrorExplicitSemantics
-               : ComplexSdpCategory::kErrorImplicitSemantics;
-  }
-
-  if (*sdp_format == SdpFormat::kComplexPlanB) {
-    return sdp_semantics_specified_
-               ? ComplexSdpCategory::kPlanBExplicitSemantics
-               : ComplexSdpCategory::kPlanBImplicitSemantics;
-  } else if (*sdp_format == SdpFormat::kComplexUnifiedPlan) {
-    return sdp_semantics_specified_
-               ? ComplexSdpCategory::kUnifiedPlanExplicitSemantics
-               : ComplexSdpCategory::kUnifiedPlanImplicitSemantics;
-  }
-
-  return absl::nullopt;
-}
-
-void RTCPeerConnection::RecordSdpCategoryAndMaybeEmitWarnings(
-    const ParsedSessionDescription& parsed_sdp) const {
-  absl::optional<ComplexSdpCategory> complex_sdp_category =
-      CheckForComplexSdp(parsed_sdp);
-  if (!complex_sdp_category || !GetExecutionContext())
-    return;
-
-  LocalDOMWindow* window = To<LocalDOMWindow>(GetExecutionContext());
-  RTCPeerConnectionController::From(*window->document())
-      .MaybeReportComplexSdp(*complex_sdp_category);
-
-  if (*complex_sdp_category == ComplexSdpCategory::kPlanBImplicitSemantics) {
-    Deprecation::CountDeprecation(
-        GetExecutionContext(),
-        WebFeature::kRTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics);
-  }
-
-  // kComplexPlanB/kComplexUnifiedPlan or null.
-  absl::optional<SdpFormat> complex_sdp_format;
-  switch (*complex_sdp_category) {
-    case ComplexSdpCategory::kPlanBExplicitSemantics:
-    case ComplexSdpCategory::kPlanBImplicitSemantics:
-      complex_sdp_format = SdpFormat::kComplexPlanB;
-      break;
-    case ComplexSdpCategory::kUnifiedPlanExplicitSemantics:
-    case ComplexSdpCategory::kUnifiedPlanImplicitSemantics:
-      complex_sdp_format = SdpFormat::kComplexUnifiedPlan;
-      break;
-    case ComplexSdpCategory::kErrorImplicitSemantics:
-    case ComplexSdpCategory::kErrorExplicitSemantics:
-      complex_sdp_format = absl::nullopt;
-      break;
-  }
-  // Complex SDP use counters go up when complex SDP is used on an
-  // RTCPeerConnection configured for that SDP format.
-  if (complex_sdp_format.has_value()) {
-    if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB &&
-        complex_sdp_format.value() == SdpFormat::kComplexPlanB) {
-      UseCounter::Count(GetExecutionContext(),
-                        WebFeature::kRTCPeerConnectionUsingComplexPlanB);
-    } else if (sdp_semantics_ == webrtc::SdpSemantics::kUnifiedPlan &&
-               complex_sdp_format.value() == SdpFormat::kComplexUnifiedPlan) {
-      UseCounter::Count(GetExecutionContext(),
-                        WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan);
-    }
-  }
-}
-
 HeapHashSet<Member<RTCIceTransport>> RTCPeerConnection::ActiveIceTransports()
     const {
   HeapHashSet<Member<RTCIceTransport>> active_transports;
@@ -1028,7 +857,6 @@
 }
 
 void RTCPeerConnection::UpdateIceConnectionState() {
-  DCHECK_EQ(webrtc::SdpSemantics::kUnifiedPlan, sdp_semantics_);
   auto new_state = ComputeIceConnectionState();
   if (ice_connection_state_ != new_state) {
     peer_handler_->TrackIceConnectionStateChange(new_state);
@@ -1080,8 +908,6 @@
   ParsedSessionDescription parsed_sdp =
       ParsedSessionDescription::Parse(session_description_init->type(), sdp);
   if (session_description_init->type() != V8RTCSdpType::Enum::kRollback) {
-    RecordSdpCategoryAndMaybeEmitWarnings(parsed_sdp);
-
     DOMException* exception = checkSdpForStateErrors(
         ExecutionContext::From(script_state), parsed_sdp);
     if (exception) {
@@ -1136,10 +962,6 @@
           ? session_description_init->type().AsString()
           : String(),
       sdp);
-  if (!session_description_init->hasType() ||
-      session_description_init->type() != V8RTCSdpType::Enum::kRollback) {
-    RecordSdpCategoryAndMaybeEmitWarnings(parsed_sdp);
-  }
   ExecutionContext* context = ExecutionContext::From(script_state);
   UseCounter::Count(context, WebFeature::kRTCPeerConnectionSetLocalDescription);
   if (success_callback && error_callback) {
@@ -1199,10 +1021,6 @@
   DCHECK(script_state->ContextIsValid());
   ParsedSessionDescription parsed_sdp =
       ParsedSessionDescription::Parse(session_description_init);
-  if (!session_description_init->hasType() ||
-      session_description_init->type() != V8RTCSdpType::Enum::kRollback) {
-    RecordSdpCategoryAndMaybeEmitWarnings(parsed_sdp);
-  }
   if (signaling_state_ ==
       webrtc::PeerConnectionInterface::SignalingState::kClosed) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -1244,10 +1062,6 @@
   DCHECK(script_state->ContextIsValid());
   ParsedSessionDescription parsed_sdp =
       ParsedSessionDescription::Parse(session_description_init);
-  if (!session_description_init->hasType() ||
-      session_description_init->type() != V8RTCSdpType::Enum::kRollback) {
-    RecordSdpCategoryAndMaybeEmitWarnings(parsed_sdp);
-  }
   ExecutionContext* context = ExecutionContext::From(script_state);
   UseCounter::Count(context,
                     WebFeature::kRTCPeerConnectionSetRemoteDescription);
@@ -1338,17 +1152,6 @@
       NOTREACHED();
   }
 
-  switch (webrtc_configuration.sdp_semantics) {
-    case webrtc::SdpSemantics::kPlanB:
-      result->setSdpSemantics("plan-b");
-      break;
-    case webrtc::SdpSemantics::kUnifiedPlan:
-      result->setSdpSemantics("unified-plan");
-      break;
-    default:
-      NOTREACHED();
-  }
-
   HeapVector<Member<RTCIceServer>> ice_servers;
   ice_servers.ReserveCapacity(
       base::checked_cast<wtf_size_t>(webrtc_configuration.servers.size()));
@@ -1734,21 +1537,12 @@
 
 MediaStreamVector RTCPeerConnection::getLocalStreams() const {
   MediaStreamVector local_streams;
-  if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB) {
-    for (const auto& sender : rtp_senders_) {
-      for (const auto& stream : sender->streams()) {
-        if (!local_streams.Contains(stream))
-          local_streams.push_back(stream);
-      }
-    }
-  } else {
-    for (const auto& transceiver : transceivers_) {
-      if (!transceiver->DirectionHasSend())
-        continue;
-      for (const auto& stream : transceiver->sender()->streams()) {
-        if (!local_streams.Contains(stream))
-          local_streams.push_back(stream);
-      }
+  for (const auto& transceiver : transceivers_) {
+    if (!transceiver->DirectionHasSend())
+      continue;
+    for (const auto& stream : transceiver->sender()->streams()) {
+      if (!local_streams.Contains(stream))
+        local_streams.push_back(stream);
     }
   }
   return local_streams;
@@ -1756,21 +1550,12 @@
 
 MediaStreamVector RTCPeerConnection::getRemoteStreams() const {
   MediaStreamVector remote_streams;
-  if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB) {
-    for (const auto& receiver : rtp_receivers_) {
-      for (const auto& stream : receiver->streams()) {
-        if (!remote_streams.Contains(stream))
-          remote_streams.push_back(stream);
-      }
-    }
-  } else {
-    for (const auto& transceiver : transceivers_) {
-      if (!transceiver->DirectionHasRecv())
-        continue;
-      for (const auto& stream : transceiver->receiver()->streams()) {
-        if (!remote_streams.Contains(stream))
-          remote_streams.push_back(stream);
-      }
+  for (const auto& transceiver : transceivers_) {
+    if (!transceiver->DirectionHasRecv())
+      continue;
+    for (const auto& stream : transceiver->receiver()->streams()) {
+      if (!remote_streams.Contains(stream))
+        remote_streams.push_back(stream);
     }
   }
   return remote_streams;
@@ -1958,11 +1743,6 @@
     const V8UnionMediaStreamTrackOrString* track_or_kind,
     const RTCRtpTransceiverInit* init,
     ExceptionState& exception_state) {
-  if (sdp_semantics_ != webrtc::SdpSemantics::kUnifiedPlan) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      kOnlySupportedInUnifiedPlanMessage);
-    return nullptr;
-  }
   if (ThrowExceptionIfSignalingStateClosed(signaling_state_, &exception_state))
     return nullptr;
   auto webrtc_init = ToRtpTransceiverInit(GetExecutionContext(), init);
@@ -2024,14 +1804,6 @@
   DCHECK(track->Component());
   if (ThrowExceptionIfSignalingStateClosed(signaling_state_, &exception_state))
     return nullptr;
-  if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB && streams.size() >= 2) {
-    // TODO(hbos): Update peer_handler_ to call the AddTrack() that returns the
-    // appropriate errors, and let the lower layers handle it.
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kNotSupportedError,
-        "Adding a track to multiple streams is not supported.");
-    return nullptr;
-  }
   for (const auto& sender : rtp_senders_) {
     if (sender->track() == track) {
       exception_state.ThrowDOMException(
@@ -2060,31 +1832,12 @@
   RegisterTrack(track);
 
   auto stream_ids = platform_transceiver->Sender()->StreamIds();
-  RTCRtpSender* sender;
-  if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB) {
-    DCHECK_EQ(platform_transceiver->ImplementationType(),
-              RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly);
-    sender =
-        CreateOrUpdateSender(platform_transceiver->Sender(), track->kind());
-  } else {
-    DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kUnifiedPlan);
-    DCHECK_EQ(platform_transceiver->ImplementationType(),
-              RTCRtpTransceiverPlatformImplementationType::kFullTransceiver);
-    RTCRtpTransceiver* transceiver =
-        CreateOrUpdateTransceiver(std::move(platform_transceiver));
-    sender = transceiver->sender();
-  }
+  RTCRtpTransceiver* transceiver =
+      CreateOrUpdateTransceiver(std::move(platform_transceiver));
+  RTCRtpSender* sender = transceiver->sender();
   // Newly created senders have no streams set, we have to set it ourselves.
   sender->set_streams(streams);
-
-  // The stream IDs should match between layers, with one exception;
-  // in Plan B if no stream was supplied, the lower layer still generates a
-  // stream which has no blink layer correspondence.
-  DCHECK(sdp_semantics_ != webrtc::SdpSemantics::kPlanB ||
-         (streams.size() == 0u && stream_ids.size() == 1u) ||
-         stream_ids.size() == streams.size());
-  DCHECK(sdp_semantics_ != webrtc::SdpSemantics::kUnifiedPlan ||
-         stream_ids.size() == streams.size());
+  DCHECK_EQ(stream_ids.size(), streams.size());
   return sender;
 }
 
@@ -2102,33 +1855,16 @@
   }
 
   auto error_or_transceiver = peer_handler_->RemoveTrack(sender->web_sender());
-  if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB) {
-    // Plan B: Was the sender removed?
-    if (!error_or_transceiver.ok()) {
-      // Operation aborted. This indicates that the sender is no longer used by
-      // the peer connection, i.e. that it was removed due to setting a remote
-      // description of type "rollback".
-      return;
-    }
-    // Successfully removing the track results in the sender's track property
-    // being nulled.
-    DCHECK(!sender->web_sender()->Track());
-    sender->SetTrack(nullptr);
-    rtp_senders_.erase(it);
-  } else {
-    // Unified Plan: Was the transceiver updated?
-    DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kUnifiedPlan);
-    if (!error_or_transceiver.ok()) {
-      ThrowExceptionFromRTCError(error_or_transceiver.error(), exception_state);
-      return;
-    }
-    if (!error_or_transceiver.value()) {
-      // There is no transceiver to update - the operation was cancelled, such
-      // as if the transceiver was rolled back.
-      return;
-    }
-    CreateOrUpdateTransceiver(error_or_transceiver.MoveValue());
+  if (!error_or_transceiver.ok()) {
+    ThrowExceptionFromRTCError(error_or_transceiver.error(), exception_state);
+    return;
   }
+  if (!error_or_transceiver.value()) {
+    // There is no transceiver to update - the operation was cancelled, such
+    // as if the transceiver was rolled back.
+    return;
+  }
+  CreateOrUpdateTransceiver(error_or_transceiver.MoveValue());
 }
 
 RTCSctpTransport* RTCPeerConnection::sctp() const {
@@ -2541,20 +2277,6 @@
   ChangeIceGatheringState(new_state);
 }
 
-void RTCPeerConnection::DidChangeIceConnectionState(
-    webrtc::PeerConnectionInterface::IceConnectionState new_state) {
-  DCHECK(!closed_);
-  DCHECK(GetExecutionContext()->IsContextThread());
-  if (sdp_semantics_ == webrtc::SdpSemantics::kUnifiedPlan) {
-    // Unified plan relies on UpdateIceConnectionState() instead.
-    return;
-  }
-  // Plan B only.
-  // TODO(https://crbug.com/1302249): Delete this.
-  peer_handler_->TrackIceConnectionStateChange(new_state);
-  ChangeIceConnectionState(new_state);
-}
-
 void RTCPeerConnection::DidChangePeerConnectionState(
     webrtc::PeerConnectionInterface::PeerConnectionState new_state) {
   DCHECK(!closed_);
@@ -2562,154 +2284,6 @@
   ChangePeerConnectionState(new_state);
 }
 
-void RTCPeerConnection::DidModifyReceiversPlanB(
-    webrtc::PeerConnectionInterface::SignalingState signaling_state,
-    Vector<std::unique_ptr<RTCRtpReceiverPlatform>> platform_receivers_added,
-    Vector<std::unique_ptr<RTCRtpReceiverPlatform>>
-        platform_receivers_removed) {
-  DCHECK(!closed_);
-  DCHECK(GetExecutionContext()->IsContextThread());
-  DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kPlanB);
-  if (signaling_state_ ==
-      webrtc::PeerConnectionInterface::SignalingState::kClosed)
-    return;
-
-  // We must complete all processing before firing events to avoid JS events
-  // influencing the algorithm or have events fire before the peer connection's
-  // state has settled.
-  HeapVector<Member<MediaStreamTrack>> mute_tracks;
-  HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>
-      remove_list;
-  HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>> add_list;
-  HeapVector<Member<RTCRtpReceiver>> track_events;
-  MediaStreamVector previous_streams = getRemoteStreams();
-
-  // Process the addition of receivers.
-  for (auto& platform_receiver : platform_receivers_added) {
-    // Create track.
-    auto* track = MakeGarbageCollected<MediaStreamTrackImpl>(
-        GetExecutionContext(), platform_receiver->Track());
-    tracks_.insert(track->Component(), track);
-    // Create or update streams.
-    HeapVector<Member<MediaStream>> streams;
-    for (const auto& stream_id : platform_receiver->StreamIds()) {
-      MediaStream* stream = getRemoteStreamById(stream_id);
-      if (!stream) {
-        // The stream is new, create it containing this track.
-        MediaStreamComponentVector audio_track_components;
-        MediaStreamTrackVector audio_tracks;
-        MediaStreamComponentVector video_track_components;
-        MediaStreamTrackVector video_tracks;
-        if (track->Component()->GetSourceType() ==
-            MediaStreamSource::kTypeAudio) {
-          audio_track_components.push_back(track->Component());
-          audio_tracks.push_back(track);
-        } else {
-          DCHECK(track->Component()->GetSourceType() ==
-                 MediaStreamSource::kTypeVideo);
-          video_track_components.push_back(track->Component());
-          video_tracks.push_back(track);
-        }
-        auto* descriptor = MakeGarbageCollected<MediaStreamDescriptor>(
-            stream_id, std::move(audio_track_components),
-            std::move(video_track_components));
-        stream = MediaStream::Create(GetExecutionContext(), descriptor,
-                                     std::move(audio_tracks),
-                                     std::move(video_tracks));
-      } else {
-        // The stream already exists, the track will be added and events fired
-        // after processing the remaining receivers.
-        add_list.push_back(std::make_pair(stream, track));
-      }
-      streams.push_back(stream);
-    }
-    DCHECK(FindReceiver(*platform_receiver) == rtp_receivers_.end());
-    RTCRtpReceiver* rtp_receiver = MakeGarbageCollected<RTCRtpReceiver>(
-        this, std::move(platform_receiver), track, streams,
-        encoded_insertable_streams_);
-    rtp_receivers_.push_back(rtp_receiver);
-    track_events.push_back(rtp_receiver);
-  }
-
-  // Process the removal of receivers.
-  for (auto& platform_receiver : platform_receivers_removed) {
-    auto* it = FindReceiver(*platform_receiver);
-    DCHECK(it != rtp_receivers_.end());
-    RTCRtpReceiver* rtp_receiver = *it;
-    auto streams = rtp_receiver->streams();
-    MediaStreamTrack* track = rtp_receiver->track();
-    rtp_receivers_.erase(it);
-
-    // The track will be removed from the stream and events fired after
-    // processing the remaining receivers.
-    for (const auto& stream : streams) {
-      remove_list.push_back(std::make_pair(stream, track));
-      if (!IsRemoteStream(stream)) {
-        stream->UnregisterObserver(this);
-      }
-    }
-
-    // The track will be muted and events fired after processing the remaining
-    // receivers.
-    mute_tracks.push_back(track);
-  }
-  MediaStreamVector current_streams = getRemoteStreams();
-
-  // Modify and fire "pc.onsignalingchange" synchronously.
-  if (signaling_state_ == webrtc::PeerConnectionInterface::kHaveLocalOffer &&
-      signaling_state == webrtc::PeerConnectionInterface::kHaveRemoteOffer) {
-    // Inject missing kStable in case of implicit rollback.
-    ChangeSignalingState(webrtc::PeerConnectionInterface::kStable, true);
-  }
-  ChangeSignalingState(signaling_state, true);
-
-  // Mute the tracks, this fires "track.onmute" synchronously.
-  for (auto& track : mute_tracks) {
-    track->Component()->Source()->SetReadyState(
-        MediaStreamSource::kReadyStateMuted);
-  }
-  // Remove/add tracks to streams, this fires "stream.onremovetrack" and
-  // "stream.onaddtrack" synchronously.
-  for (auto& pair : remove_list) {
-    auto& stream = pair.first;
-    auto& track = pair.second;
-    if (stream->getTracks().Contains(track)) {
-      stream->RemoveTrackAndFireEvents(
-          track,
-          MediaStreamDescriptorClient::DispatchEventTiming::kImmediately);
-    }
-  }
-  for (auto& pair : add_list) {
-    auto& stream = pair.first;
-    auto& track = pair.second;
-    if (!stream->getTracks().Contains(track)) {
-      stream->AddTrackAndFireEvents(
-          track,
-          MediaStreamDescriptorClient::DispatchEventTiming::kImmediately);
-    }
-  }
-
-  // Legacy APIs: "pc.onaddstream" and "pc.onremovestream".
-  for (const auto& current_stream : current_streams) {
-    if (!previous_streams.Contains(current_stream)) {
-      MaybeDispatchEvent(MakeGarbageCollected<MediaStreamEvent>(
-          event_type_names::kAddstream, current_stream));
-    }
-  }
-  for (const auto& previous_stream : previous_streams) {
-    if (!current_streams.Contains(previous_stream)) {
-      MaybeDispatchEvent(MakeGarbageCollected<MediaStreamEvent>(
-          event_type_names::kRemovestream, previous_stream));
-    }
-  }
-
-  // Fire "pc.ontrack" synchronously.
-  for (auto& rtp_receiver : track_events) {
-    MaybeDispatchEvent(MakeGarbageCollected<RTCTrackEvent>(
-        rtp_receiver, rtp_receiver->track(), rtp_receiver->streams(), nullptr));
-  }
-}
-
 void RTCPeerConnection::DidModifySctpTransport(
     WebRTCSctpTransportSnapshot snapshot) {
   if (!snapshot.transport) {
@@ -2793,15 +2367,13 @@
         mute_tracks.push_back(transceiver->receiver()->track());
     }
   }
-  if (sdp_semantics_ == webrtc::SdpSemantics::kUnifiedPlan) {
-    // Update the rtp_senders_ and rtp_receivers_ members to only contain
-    // senders and receivers that are in the current set of transceivers.
-    rtp_senders_.clear();
-    rtp_receivers_.clear();
-    for (auto& transceiver : transceivers_) {
-      rtp_senders_.push_back(transceiver->sender());
-      rtp_receivers_.push_back(transceiver->receiver());
-    }
+  // Update the rtp_senders_ and rtp_receivers_ members to only contain
+  // senders and receivers that are in the current set of transceivers.
+  rtp_senders_.clear();
+  rtp_receivers_.clear();
+  for (auto& transceiver : transceivers_) {
+    rtp_senders_.push_back(transceiver->sender());
+    rtp_receivers_.push_back(transceiver->receiver());
   }
 
   MediaStreamVector current_streams = getRemoteStreams();
@@ -2875,9 +2447,7 @@
   // Note - this must be done every time the set of ICE transports happens.
   // At the moment this only happens in SLD/SRD, and this function is called
   // whenever these functions complete.
-  if (sdp_semantics_ == webrtc::SdpSemantics::kUnifiedPlan) {
-    UpdateIceConnectionState();
-  }
+  UpdateIceConnectionState();
 }
 
 void RTCPeerConnection::SetAssociatedMediaStreams(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index c052685..4f0eaff 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -91,8 +91,6 @@
 class V8UnionMediaStreamTrackOrString;
 class V8VoidFunction;
 
-extern const char kOnlySupportedInUnifiedPlanMessage[];
-
 class MODULES_EXPORT RTCPeerConnection final
     : public EventTargetWithInlineData,
       public RTCPeerConnectionHandlerClient,
@@ -113,7 +111,6 @@
 
   RTCPeerConnection(ExecutionContext*,
                     webrtc::PeerConnectionInterface::RTCConfiguration,
-                    bool sdp_semantics_specified,
                     bool encoded_insertable_streams,
                     GoogMediaConstraints*,
                     ExceptionState&);
@@ -298,15 +295,8 @@
       RTCSessionDescriptionPlatform* current_remote_description) override;
   void DidChangeIceGatheringState(
       webrtc::PeerConnectionInterface::IceGatheringState) override;
-  void DidChangeIceConnectionState(
-      webrtc::PeerConnectionInterface::IceConnectionState) override;
   void DidChangePeerConnectionState(
       webrtc::PeerConnectionInterface::PeerConnectionState) override;
-  void DidModifyReceiversPlanB(
-      webrtc::PeerConnectionInterface::SignalingState,
-      Vector<std::unique_ptr<RTCRtpReceiverPlatform>> platform_receivers_added,
-      Vector<std::unique_ptr<RTCRtpReceiverPlatform>>
-          platform_receivers_removed) override;
   void DidModifySctpTransport(WebRTCSctpTransportSnapshot) override;
   void DidModifyTransceivers(webrtc::PeerConnectionInterface::SignalingState,
                              Vector<std::unique_ptr<RTCRtpTransceiverPlatform>>,
@@ -335,17 +325,6 @@
   static int PeerConnectionCount();
   static int PeerConnectionCountLimit();
 
-  // SLD/SRD Helper method, public for testing.
-  // This function returns a value that indicates if complex SDP is being used
-  // and whether a format is explicitly specified. If the SDP is not complex or
-  // it could not be parsed, absl::nullopt is returned.
-  // When "Complex" SDP (i.e., SDP that has multiple tracks) is used without
-  // explicitly specifying the SDP format, there may be errors if the
-  // application assumes a format that differs from the actual default format.
-  absl::optional<ComplexSdpCategory> CheckForComplexSdp(
-      const ParsedSessionDescription&) const;
-  void NoteVoidRequestCompleted(RTCSetSessionDescriptionOperation operation,
-                                bool success);
   static void GenerateCertificateCompleted(
       ScriptPromiseResolver* resolver,
       rtc::scoped_refptr<rtc::RTCCertificate> certificate);
@@ -354,8 +333,6 @@
   // state.
   void UpdateIceConnectionState();
 
-  webrtc::SdpSemantics sdp_semantics() { return sdp_semantics_; }
-
   bool encoded_insertable_streams() { return encoded_insertable_streams_; }
 
   void Trace(Visitor*) const override;
@@ -507,8 +484,6 @@
 
   DOMException* checkSdpForStateErrors(ExecutionContext*,
                                        const ParsedSessionDescription&);
-  void RecordSdpCategoryAndMaybeEmitWarnings(
-      const ParsedSessionDescription&) const;
 
   HeapHashSet<Member<RTCIceTransport>> ActiveIceTransports() const;
 
@@ -598,17 +573,6 @@
 
   Member<RTCSctpTransport> sctp_transport_;
 
-  // In Plan B, senders and receivers are added or removed independently of one
-  // another. In Unified Plan, senders and receivers are created in pairs as
-  // transceivers. Transceivers may become inactive, but are never removed.
-  // The value of this member affects the behavior of some methods and what
-  // information is surfaced from webrtc. This has the value "kPlanB" or
-  // "kUnifiedPlan", if constructed with "kDefault" it is translated to one or
-  // the other.
-  webrtc::SdpSemantics sdp_semantics_;
-  // Whether sdpSemantics was specified at construction.
-  bool sdp_semantics_specified_;
-
   // Blink and WebRTC timestamp diff.
   const base::TimeDelta blink_webrtc_time_diff_;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
index 8166a6f..35a1ab2 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
@@ -532,19 +532,6 @@
   return usage_count;
 }
 
-bool IsRemoteStream(
-    const std::vector<std::unique_ptr<blink::RTCRtpReceiverImpl>>&
-        rtp_receivers,
-    const std::string& stream_id) {
-  for (const auto& receiver : rtp_receivers) {
-    for (const auto& receiver_stream_id : receiver->state().stream_ids()) {
-      if (stream_id == receiver_stream_id)
-        return true;
-    }
-  }
-  return false;
-}
-
 MediaStreamTrackMetrics::Kind MediaStreamTrackMetricsKind(
     const MediaStreamComponent* component) {
   return component->GetSourceType() == MediaStreamSource::kTypeAudio
@@ -649,14 +636,12 @@
       PeerConnectionTracker* tracker,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       PeerConnectionTracker::Action action,
-      webrtc::SdpSemantics sdp_semantics,
       bool is_rollback)
       : handler_(handler),
         main_thread_(task_runner),
         web_request_(web_request),
         tracker_(tracker),
         action_(action),
-        sdp_semantics_(sdp_semantics),
         is_rollback_(is_rollback) {}
 
   void OnSetDescriptionComplete(
@@ -726,15 +711,8 @@
           std::move(current_remote_description));
     }
 
-    // Process the rest of the state changes differently depending on SDP
-    // semantics. This fires JS events could cause |handler_| to become null.
-    if (sdp_semantics_ == webrtc::SdpSemantics::kPlanB) {
-      ProcessStateChangesPlanB(std::move(states));
-    } else {
-      DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kUnifiedPlan);
-      ProcessStateChangesUnifiedPlan(std::move(states));
-    }
-
+    // This fires JS events and could cause |handler_| to become null.
+    ProcessStateChanges(std::move(states));
     ResolvePromise();
   }
 
@@ -746,63 +724,7 @@
     web_request_ = nullptr;
   }
 
-  void ProcessStateChangesPlanB(WebRtcSetDescriptionObserver::States states) {
-    DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kPlanB);
-    if (!handler_)
-      return;
-
-    // Determine which receivers have been removed before processing the
-    // removal as to not invalidate the iterator.
-    Vector<blink::RTCRtpReceiverImpl*> removed_receivers;
-    for (const auto& receiver : handler_->rtp_receivers_) {
-      if (ReceiverWasRemoved(*receiver, states.transceiver_states))
-        removed_receivers.push_back(receiver.get());
-    }
-
-    // Process the addition and removal of remote receivers/tracks.
-    Vector<blink::RtpReceiverState> added_receiver_states;
-    for (auto& transceiver_state : states.transceiver_states) {
-      if (ReceiverWasAdded(transceiver_state)) {
-        added_receiver_states.push_back(transceiver_state.MoveReceiverState());
-      }
-    }
-    Vector<uintptr_t> removed_receiver_ids;
-    for (const auto* removed_receiver : removed_receivers) {
-      removed_receiver_ids.push_back(blink::RTCRtpReceiverImpl::getId(
-          removed_receiver->state().webrtc_receiver().get()));
-    }
-    // |handler_| can become null after this call.
-    handler_->OnReceiversModifiedPlanB(states.signaling_state,
-                                       std::move(added_receiver_states),
-                                       std::move(removed_receiver_ids));
-  }
-
-  bool ReceiverWasAdded(const blink::RtpTransceiverState& transceiver_state) {
-    DCHECK(handler_);
-    uintptr_t receiver_id = blink::RTCRtpReceiverImpl::getId(
-        transceiver_state.receiver_state()->webrtc_receiver().get());
-    for (const auto& receiver : handler_->rtp_receivers_) {
-      if (receiver->Id() == receiver_id)
-        return false;
-    }
-    return true;
-  }
-
-  bool ReceiverWasRemoved(
-      const blink::RTCRtpReceiverImpl& receiver,
-      const std::vector<blink::RtpTransceiverState>& transceiver_states) {
-    for (const auto& transceiver_state : transceiver_states) {
-      if (transceiver_state.receiver_state()->webrtc_receiver() ==
-          receiver.state().webrtc_receiver()) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  void ProcessStateChangesUnifiedPlan(
-      WebRtcSetDescriptionObserver::States states) {
-    DCHECK_EQ(sdp_semantics_, webrtc::SdpSemantics::kUnifiedPlan);
+  void ProcessStateChanges(WebRtcSetDescriptionObserver::States states) {
     if (handler_) {
       handler_->OnModifySctpTransport(std::move(states.sctp_transport_state));
     }
@@ -819,7 +741,6 @@
   Persistent<blink::RTCVoidRequest> web_request_;
   CrossThreadWeakPersistent<PeerConnectionTracker> tracker_;
   PeerConnectionTracker::Action action_;
-  webrtc::SdpSemantics sdp_semantics_;
   bool is_rollback_;
 };
 
@@ -1280,11 +1201,7 @@
     blink::TransceiverStateSurfacer* transceiver_state_surfacer) {
   native_peer_connection_->CreateOffer(observer, offer_options);
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
-      transceivers =
-          configuration_.sdp_semantics == webrtc::SdpSemantics::kUnifiedPlan
-              ? native_peer_connection_->GetTransceivers()
-              : std::vector<
-                    rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>();
+      transceivers = native_peer_connection_->GetTransceivers();
   transceiver_state_surfacer->Initialize(
       native_peer_connection_, track_adapter_map_, std::move(transceivers));
 }
@@ -1329,17 +1246,13 @@
           weak_factory_.GetWeakPtr(), request, peer_connection_tracker_,
           task_runner_,
           PeerConnectionTracker::kActionSetLocalDescriptionImplicit,
-          configuration_.sdp_semantics,
           /*is_rollback=*/true);
 
-  // Surfacing transceivers is not applicable in Plan B.
-  bool surface_receivers_only =
-      (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB);
   rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface>
       webrtc_observer(WebRtcSetLocalDescriptionObserverHandler::Create(
                           task_runner_, signaling_thread(),
                           native_peer_connection_, track_adapter_map_,
-                          content_observer, surface_receivers_only)
+                          content_observer)
                           .get());
 
   PostCrossThreadTask(
@@ -1408,15 +1321,13 @@
       base::MakeRefCounted<WebRtcSetDescriptionObserverImpl>(
           weak_factory_.GetWeakPtr(), request, peer_connection_tracker_,
           task_runner_, PeerConnectionTracker::kActionSetLocalDescription,
-          configuration_.sdp_semantics, type == "rollback");
+          type == "rollback");
 
-  bool surface_receivers_only =
-      (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB);
   rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface>
       webrtc_observer(WebRtcSetLocalDescriptionObserverHandler::Create(
                           task_runner_, signaling_thread(),
                           native_peer_connection_, track_adapter_map_,
-                          content_observer, surface_receivers_only)
+                          content_observer)
                           .get());
 
   PostCrossThreadTask(
@@ -1487,15 +1398,13 @@
       base::MakeRefCounted<WebRtcSetDescriptionObserverImpl>(
           weak_factory_.GetWeakPtr(), request, peer_connection_tracker_,
           task_runner_, PeerConnectionTracker::kActionSetRemoteDescription,
-          configuration_.sdp_semantics, type == "rollback");
+          type == "rollback");
 
-  bool surface_receivers_only =
-      (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB);
   rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>
       webrtc_observer(WebRtcSetRemoteDescriptionObserverHandler::Create(
                           task_runner_, signaling_thread(),
                           native_peer_connection_, track_adapter_map_,
-                          content_observer, surface_receivers_only)
+                          content_observer)
                           .get());
 
   PostCrossThreadTask(
@@ -1529,7 +1438,6 @@
   new_configuration.type = blink_config.type;
   new_configuration.bundle_policy = blink_config.bundle_policy;
   new_configuration.rtcp_mux_policy = blink_config.rtcp_mux_policy;
-  new_configuration.sdp_semantics = blink_config.sdp_semantics;
   new_configuration.certificates = blink_config.certificates;
   new_configuration.ice_candidate_pool_size =
       blink_config.ice_candidate_pool_size;
@@ -1711,7 +1619,6 @@
 RTCPeerConnectionHandler::AddTransceiverWithTrack(
     MediaStreamComponent* component,
     const webrtc::RtpTransceiverInit& init) {
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
       track_ref = track_adapter_map_->GetOrCreateLocalTrackAdapter(component);
@@ -1768,7 +1675,6 @@
 RTCPeerConnectionHandler::AddTransceiverWithKind(
     const String& kind,
     const webrtc::RtpTransceiverInit& init) {
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   cricket::MediaType media_type;
   if (kind == webrtc::MediaStreamTrackInterface::kAudioKind) {
@@ -1839,9 +1745,7 @@
     stream_ids[i] = descriptors[i]->Id().Utf8();
 
   // Invoke native AddTrack() on the signaling thread and surface the resulting
-  // transceiver (Plan B: sender only).
-  // TODO(hbos): Implement and surface full transceiver support under Unified
-  // Plan. https://crbug.com/777617
+  // transceiver.
   blink::TransceiverStateSurfacer transceiver_state_surfacer(
       task_runner_, signaling_thread());
   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>>
@@ -1869,27 +1773,10 @@
   auto transceiver_state = std::move(transceiver_states[0]);
 
   std::unique_ptr<RTCRtpTransceiverPlatform> platform_transceiver;
-  if (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB) {
-    // Plan B: Create sender only.
-    DCHECK(transceiver_state.sender_state());
-    auto webrtc_sender = transceiver_state.sender_state()->webrtc_sender();
-    DCHECK(FindSender(blink::RTCRtpSenderImpl::getId(webrtc_sender.get())) ==
-           rtp_senders_.end());
-    blink::RtpSenderState sender_state = transceiver_state.MoveSenderState();
-    DCHECK(sender_state.is_initialized());
-    rtp_senders_.push_back(std::make_unique<blink::RTCRtpSenderImpl>(
-        native_peer_connection_, track_adapter_map_, std::move(sender_state),
-        encoded_insertable_streams_));
-    MaybeCreateThermalUmaListner();
-    platform_transceiver = std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
-        std::make_unique<blink::RTCRtpSenderImpl>(*rtp_senders_.back().get()));
-  } else {
-    DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
-    // Unified Plan: Create or recycle a transceiver.
-    auto transceiver = CreateOrUpdateTransceiver(
-        std::move(transceiver_state), blink::TransceiverStateUpdateMode::kAll);
-    platform_transceiver = std::move(transceiver);
-  }
+  // Create or recycle a transceiver.
+  auto transceiver = CreateOrUpdateTransceiver(
+      std::move(transceiver_state), blink::TransceiverStateUpdateMode::kAll);
+  platform_transceiver = std::move(transceiver);
   if (peer_connection_tracker_) {
     size_t transceiver_index = GetTransceiverIndex(*platform_transceiver.get());
     peer_connection_tracker_->TrackAddTransceiver(
@@ -1917,24 +1804,15 @@
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
   if (error_or_sender->ok()) {
     auto sender = error_or_sender->value();
-    if (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB) {
-      transceivers = {rtc::scoped_refptr<webrtc::RtpTransceiverInterface>(
-          new blink::SurfaceSenderStateOnly(sender))};
-    } else {
-      DCHECK_EQ(configuration_.sdp_semantics,
-                webrtc::SdpSemantics::kUnifiedPlan);
-      rtc::scoped_refptr<webrtc::RtpTransceiverInterface>
-          transceiver_for_sender = nullptr;
-      for (const auto& transceiver :
-           native_peer_connection_->GetTransceivers()) {
-        if (transceiver->sender() == sender) {
-          transceiver_for_sender = transceiver;
-          break;
-        }
+    rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver_for_sender;
+    for (const auto& transceiver : native_peer_connection_->GetTransceivers()) {
+      if (transceiver->sender() == sender) {
+        transceiver_for_sender = transceiver;
+        break;
       }
-      DCHECK(transceiver_for_sender);
-      transceivers = {transceiver_for_sender};
     }
+    DCHECK(transceiver_for_sender);
+    transceivers = {transceiver_for_sender};
   }
   transceiver_state_surfacer->Initialize(
       native_peer_connection_, track_adapter_map_, std::move(transceivers));
@@ -1944,61 +1822,6 @@
 RTCPeerConnectionHandler::RemoveTrack(blink::RTCRtpSenderPlatform* web_sender) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::RemoveTrack");
-  if (configuration_.sdp_semantics == webrtc::SdpSemantics::kPlanB) {
-    if (RemoveTrackPlanB(web_sender)) {
-      // In Plan B, null indicates success.
-      std::unique_ptr<RTCRtpTransceiverPlatform> platform_transceiver;
-      return std::move(platform_transceiver);
-    }
-    // TODO(hbos): Surface RTCError from third_party/webrtc when
-    // peerconnectioninterface.h is updated. https://crbug.com/webrtc/9534
-    return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE);
-  }
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
-  return RemoveTrackUnifiedPlan(web_sender);
-}
-
-bool RTCPeerConnectionHandler::RemoveTrackPlanB(
-    blink::RTCRtpSenderPlatform* web_sender) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kPlanB);
-  auto* track = web_sender->Track();
-  auto it = FindSender(web_sender->Id());
-  if (it == rtp_senders_.end())
-    return false;
-  if (!(*it)->RemoveFromPeerConnection(native_peer_connection_.get()))
-    return false;
-  if (track) {
-    track_metrics_.RemoveTrack(MediaStreamTrackMetrics::Direction::kSend,
-                               MediaStreamTrackMetricsKind(track),
-                               track->Id().Utf8());
-  }
-  if (peer_connection_tracker_) {
-    auto sender_only_transceiver =
-        std::make_unique<blink::RTCRtpSenderOnlyTransceiver>(
-            std::make_unique<blink::RTCRtpSenderImpl>(*it->get()));
-    size_t sender_index = GetTransceiverIndex(*sender_only_transceiver);
-    peer_connection_tracker_->TrackRemoveTransceiver(
-        this, PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack,
-        *sender_only_transceiver.get(), sender_index);
-  }
-  std::vector<std::string> stream_ids = (*it)->state().stream_ids();
-  rtp_senders_.erase(it);
-  for (const auto& stream_id : stream_ids) {
-    if (GetLocalStreamUsageCount(rtp_senders_, stream_id) == 0u) {
-      // This was the last occurrence of this stream.
-      blink::PerSessionWebRTCAPIMetrics::GetInstance()
-          ->DecrementStreamCounter();
-    }
-  }
-  return true;
-}
-
-webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
-RTCPeerConnectionHandler::RemoveTrackUnifiedPlan(
-    blink::RTCRtpSenderPlatform* web_sender) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   auto it = FindSender(web_sender->Id());
   if (it == rtp_senders_.end())
     return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER);
@@ -2008,14 +1831,13 @@
   blink::TransceiverStateSurfacer transceiver_state_surfacer(
       task_runner_, signaling_thread());
   absl::optional<webrtc::RTCError> result;
-  // TODO(hta): Figure out syntax for using an OnceClosure here.
   RunSynchronousOnceClosureOnSignalingThread(
-      base::BindOnce(
-          &RTCPeerConnectionHandler::RemoveTrackUnifiedPlanOnSignalingThread,
-          base::Unretained(this), base::RetainedRef(webrtc_sender.get()),
-          base::Unretained(&transceiver_state_surfacer),
-          base::Unretained(&result)),
-      "RemoveTrackUnifiedPlanOnSignalingThread");
+      base::BindOnce(&RTCPeerConnectionHandler::RemoveTrackOnSignalingThread,
+                     base::Unretained(this),
+                     base::RetainedRef(webrtc_sender.get()),
+                     base::Unretained(&transceiver_state_surfacer),
+                     base::Unretained(&result)),
+      "RemoveTrackOnSignalingThread");
   DCHECK(transceiver_state_surfacer.is_initialized());
   if (!result || !result->ok()) {
     // Don't leave the surfacer in a pending state.
@@ -2045,7 +1867,7 @@
   return platform_transceiver;
 }
 
-void RTCPeerConnectionHandler::RemoveTrackUnifiedPlanOnSignalingThread(
+void RTCPeerConnectionHandler::RemoveTrackOnSignalingThread(
     webrtc::RtpSenderInterface* sender,
     blink::TransceiverStateSurfacer* transceiver_state_surfacer,
     absl::optional<webrtc::RTCError>* result) {
@@ -2296,7 +2118,11 @@
     peer_connection_tracker_->TrackSignalingStateChange(this, new_state);
 }
 
-// Called any time the IceConnectionState changes
+// Called any time the lower layer IceConnectionState changes, which is NOT in
+// sync with the iceConnectionState that is exposed to JavaScript (that one is
+// computed by RTCPeerConnection::UpdateIceConnectionState)! This method is
+// purely used for UMA reporting. We may want to consider wiring this up to
+// UpdateIceConnectionState() instead...
 void RTCPeerConnectionHandler::OnIceConnectionChange(
     webrtc::PeerConnectionInterface::IceConnectionState new_state) {
   TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::OnIceConnectionChange");
@@ -2324,8 +2150,6 @@
   }
 
   track_metrics_.IceConnectionChange(new_state);
-  if (!is_closed_)
-    client_->DidChangeIceConnectionState(new_state);
 }
 
 void RTCPeerConnectionHandler::TrackIceConnectionStateChange(
@@ -2384,93 +2208,6 @@
   client_->NegotiationNeeded();
 }
 
-void RTCPeerConnectionHandler::OnReceiversModifiedPlanB(
-    webrtc::PeerConnectionInterface::SignalingState signaling_state,
-    Vector<blink::RtpReceiverState> added_receiver_states,
-    Vector<uintptr_t> removed_receiver_ids) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::OnReceiversModifiedPlanB");
-
-  // Process the addition of receivers.
-  Vector<std::unique_ptr<RTCRtpReceiverPlatform>> platform_receivers_added;
-  for (blink::RtpReceiverState& receiver_state : added_receiver_states) {
-    DCHECK(receiver_state.is_initialized());
-    auto* track = receiver_state.track_ref()->track();
-    // Update metrics.
-    track_metrics_.AddTrack(MediaStreamTrackMetrics::Direction::kReceive,
-                            MediaStreamTrackMetricsKind(track),
-                            track->Id().Utf8());
-    for (const auto& stream_id : receiver_state.stream_ids()) {
-      // New remote stream?
-      if (!IsRemoteStream(rtp_receivers_, stream_id)) {
-        blink::PerSessionWebRTCAPIMetrics::GetInstance()
-            ->IncrementStreamCounter();
-      }
-    }
-    uintptr_t receiver_id = blink::RTCRtpReceiverImpl::getId(
-        receiver_state.webrtc_receiver().get());
-    DCHECK(FindReceiver(receiver_id) == rtp_receivers_.end());
-    auto rtp_receiver = std::make_unique<blink::RTCRtpReceiverImpl>(
-        native_peer_connection_, std::move(receiver_state),
-        encoded_insertable_streams_);
-    rtp_receivers_.push_back(
-        std::make_unique<blink::RTCRtpReceiverImpl>(*rtp_receiver));
-    if (peer_connection_tracker_) {
-      auto receiver_only_transceiver =
-          std::make_unique<blink::RTCRtpReceiverOnlyTransceiver>(
-              std::make_unique<blink::RTCRtpReceiverImpl>(*rtp_receiver));
-      size_t receiver_index = GetTransceiverIndex(*receiver_only_transceiver);
-      peer_connection_tracker_->TrackAddTransceiver(
-          this,
-          PeerConnectionTracker::TransceiverUpdatedReason::
-              kSetRemoteDescription,
-          *receiver_only_transceiver.get(), receiver_index);
-    }
-
-    platform_receivers_added.push_back(rtp_receiver->ShallowCopy());
-  }
-
-  // Process the removal of receivers.
-  Vector<std::unique_ptr<RTCRtpReceiverPlatform>> platform_receivers_removed;
-  for (uintptr_t receiver_id : removed_receiver_ids) {
-    auto it = FindReceiver(receiver_id);
-    DCHECK(it != rtp_receivers_.end());
-    auto receiver = std::make_unique<blink::RTCRtpReceiverImpl>(*(*it));
-    // Update metrics.
-    track_metrics_.RemoveTrack(MediaStreamTrackMetrics::Direction::kReceive,
-                               MediaStreamTrackMetricsKind(receiver->Track()),
-                               receiver->Track()->Id().Utf8());
-    if (peer_connection_tracker_) {
-      auto receiver_only_transceiver =
-          std::make_unique<blink::RTCRtpReceiverOnlyTransceiver>(
-              std::make_unique<blink::RTCRtpReceiverImpl>(*receiver));
-      size_t receiver_index = GetTransceiverIndex(*receiver_only_transceiver);
-      peer_connection_tracker_->TrackRemoveTransceiver(
-          this,
-          PeerConnectionTracker::TransceiverUpdatedReason::
-              kSetRemoteDescription,
-          *receiver_only_transceiver.get(), receiver_index);
-    }
-    rtp_receivers_.erase(it);
-    for (const auto& stream_id : receiver->state().stream_ids()) {
-      // This was the last occurence of the stream?
-      if (!IsRemoteStream(rtp_receivers_, stream_id)) {
-        blink::PerSessionWebRTCAPIMetrics::GetInstance()
-            ->IncrementStreamCounter();
-      }
-    }
-
-    platform_receivers_removed.push_back(std::move(receiver));
-  }
-
-  // Surface changes to RTCPeerConnection.
-  if (!is_closed_) {
-    client_->DidModifyReceiversPlanB(signaling_state,
-                                     std::move(platform_receivers_added),
-                                     std::move(platform_receivers_removed));
-  }
-}
-
 void RTCPeerConnectionHandler::OnModifySctpTransport(
     blink::WebRTCSctpTransportSnapshot state) {
   if (client_)
@@ -2483,7 +2220,6 @@
     bool is_remote_description,
     bool is_rollback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   Vector<std::unique_ptr<RTCRtpTransceiverPlatform>> platform_transceivers(
       base::checked_cast<WTF::wtf_size_t>(transceiver_states.size()));
   PeerConnectionTracker::TransceiverUpdatedReason update_reason =
@@ -2683,27 +2419,9 @@
 
 size_t RTCPeerConnectionHandler::GetTransceiverIndex(
     const RTCRtpTransceiverPlatform& platform_transceiver) {
-  if (platform_transceiver.ImplementationType() ==
-      RTCRtpTransceiverPlatformImplementationType::kFullTransceiver) {
-    for (size_t i = 0; i < rtp_transceivers_.size(); ++i) {
-      if (platform_transceiver.Id() == rtp_transceivers_[i]->Id())
-        return i;
-    }
-  } else if (platform_transceiver.ImplementationType() ==
-             RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly) {
-    const auto web_sender = platform_transceiver.Sender();
-    for (size_t i = 0; i < rtp_senders_.size(); ++i) {
-      if (web_sender->Id() == rtp_senders_[i]->Id())
-        return i;
-    }
-  } else {
-    RTC_DCHECK(platform_transceiver.ImplementationType() ==
-               RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly);
-    const auto platform_receiver = platform_transceiver.Receiver();
-    for (size_t i = 0; i < rtp_receivers_.size(); ++i) {
-      if (platform_receiver->Id() == rtp_receivers_[i]->Id())
-        return i;
-    }
+  for (size_t i = 0; i < rtp_transceivers_.size(); ++i) {
+    if (platform_transceiver.Id() == rtp_transceivers_[i]->Id())
+      return i;
   }
   NOTREACHED();
   return 0u;
@@ -2713,7 +2431,6 @@
 RTCPeerConnectionHandler::CreateOrUpdateTransceiver(
     blink::RtpTransceiverState transceiver_state,
     blink::TransceiverStateUpdateMode update_mode) {
-  DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   DCHECK(transceiver_state.is_initialized());
   DCHECK(transceiver_state.sender_state());
   DCHECK(transceiver_state.receiver_state());
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h
index 84f16f9..435fb8e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h
@@ -306,10 +306,6 @@
   void OnIceGatheringChange(
       webrtc::PeerConnectionInterface::IceGatheringState new_state);
   void OnNegotiationNeededEvent(uint32_t event_id);
-  void OnReceiversModifiedPlanB(
-      webrtc::PeerConnectionInterface::SignalingState signaling_state,
-      Vector<blink::RtpReceiverState> receiver_state,
-      Vector<uintptr_t> receiver_id);
   void OnModifySctpTransport(blink::WebRTCSctpTransportSnapshot state);
   void OnModifyTransceivers(
       webrtc::PeerConnectionInterface::SignalingState signaling_state,
@@ -384,14 +380,11 @@
       blink::TransceiverStateSurfacer* transceiver_state_surfacer,
       webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>>*
           error_or_sender);
-  bool RemoveTrackPlanB(blink::RTCRtpSenderPlatform* web_sender);
-  webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
-  RemoveTrackUnifiedPlan(blink::RTCRtpSenderPlatform* web_sender);
   // Helper function to remove a track on the signaling thread.
   // Updates the entire transceiver state.
   // The result will be absl::nullopt if the operation is cancelled,
   // and no change to the state will be made.
-  void RemoveTrackUnifiedPlanOnSignalingThread(
+  void RemoveTrackOnSignalingThread(
       webrtc::RtpSenderInterface* sender,
       blink::TransceiverStateSurfacer* transceiver_state_surfacer,
       absl::optional<webrtc::RTCError>* result);
@@ -458,10 +451,6 @@
   // needs to reference it, and automatically disposed when there are no longer
   // any components referencing it.
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
-  // In Plan B, senders and receivers are added or removed independently of one
-  // another. In Unified Plan, senders and receivers are created in pairs as
-  // transceivers. Transceivers may become inactive, but are never removed.
-  // TODO(hbos): Implement transceiver behaviors. https://crbug.com/777617
   // Blink layer correspondents of |webrtc::RtpSenderInterface|.
   std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>> rtp_senders_;
   // Blink layer correspondents of |webrtc::RtpReceiverInterface|.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc
index 9994cc6d..ae1538a2 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc
@@ -314,11 +314,10 @@
 
     pc_handler_ = CreateRTCPeerConnectionHandlerUnderTest();
     mock_tracker_ = MakeGarbageCollected<NiceMock<MockPeerConnectionTracker>>();
-    webrtc::PeerConnectionInterface::RTCConfiguration config;
-    config.sdp_semantics = webrtc::SdpSemantics::kPlanB;
     DummyExceptionStateForTesting exception_state;
-    EXPECT_TRUE(pc_handler_->InitializeForTest(config, mock_tracker_.Get(),
-                                               exception_state));
+    EXPECT_TRUE(pc_handler_->InitializeForTest(
+        webrtc::PeerConnectionInterface::RTCConfiguration(),
+        mock_tracker_.Get(), exception_state));
     mock_peer_connection_ = pc_handler_->native_peer_connection();
     ASSERT_TRUE(mock_peer_connection_);
     EXPECT_CALL(*mock_peer_connection_, Close());
@@ -438,9 +437,6 @@
       auto error_or_transceiver = pc_handler_->AddTrack(
           component, MediaStreamDescriptorVector({descriptor}));
       if (error_or_transceiver.ok()) {
-        DCHECK_EQ(
-            error_or_transceiver.value()->ImplementationType(),
-            RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly);
         auto sender = error_or_transceiver.value()->Sender();
         senders_.push_back(std::unique_ptr<blink::RTCRtpSenderImpl>(
             static_cast<blink::RTCRtpSenderImpl*>(sender.release())));
@@ -450,9 +446,6 @@
       auto error_or_transceiver = pc_handler_->AddTrack(
           component, MediaStreamDescriptorVector({descriptor}));
       if (error_or_transceiver.ok()) {
-        DCHECK_EQ(
-            error_or_transceiver.value()->ImplementationType(),
-            RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly);
         auto sender = error_or_transceiver.value()->Sender();
         senders_.push_back(std::unique_ptr<blink::RTCRtpSenderImpl>(
             static_cast<blink::RTCRtpSenderImpl*>(sender.release())));
@@ -642,17 +635,13 @@
   pc_handler_->observer()->OnIceGatheringChange(
       webrtc::PeerConnectionInterface::kIceGatheringNew);
 
-  EXPECT_CALL(*mock_client_.get(), DidChangeIceConnectionState(_)).Times(0);
-  pc_handler_->observer()->OnIceConnectionChange(
-      webrtc::PeerConnectionInterface::kIceConnectionDisconnected);
-
-  EXPECT_CALL(*mock_client_.get(), DidModifyReceiversPlanBForMock(_, _, _))
+  EXPECT_CALL(*mock_client_.get(), DidModifyTransceiversForMock(_, _, _))
       .Times(0);
   rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
       AddRemoteMockMediaStream("remote_stream", "video", "audio"));
   InvokeOnAddStream(remote_stream);
 
-  EXPECT_CALL(*mock_client_.get(), DidModifyReceiversPlanBForMock(_, _, _))
+  EXPECT_CALL(*mock_client_.get(), DidModifyTransceiversForMock(_, _, _))
       .Times(0);
   InvokeOnRemoveStream(remote_stream);
 
@@ -835,12 +824,6 @@
           pc_handler_.get(),
           PeerConnectionTracker::TransceiverUpdatedReason::kAddTrack, _, _))
       .Times(2);
-  EXPECT_CALL(
-      *mock_tracker_.Get(),
-      TrackRemoveTransceiver(
-          pc_handler_.get(),
-          PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack, _, _))
-      .Times(2);
   EXPECT_TRUE(AddStream(local_stream));
   EXPECT_EQ(stream_label.Utf8(), mock_peer_connection_->stream_label());
   EXPECT_EQ(2u, mock_peer_connection_->GetSenders().size());
@@ -1061,53 +1044,6 @@
   EXPECT_EQ(defined_stats_count, 1);
 }
 
-TEST_F(RTCPeerConnectionHandlerTest, OnIceConnectionChange) {
-  testing::InSequence sequence;
-
-  webrtc::PeerConnectionInterface::IceConnectionState new_state =
-      webrtc::PeerConnectionInterface::kIceConnectionNew;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionNew));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-
-  new_state = webrtc::PeerConnectionInterface::kIceConnectionChecking;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionChecking));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-
-  new_state = webrtc::PeerConnectionInterface::kIceConnectionConnected;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionConnected));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-
-  new_state = webrtc::PeerConnectionInterface::kIceConnectionCompleted;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionCompleted));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-
-  new_state = webrtc::PeerConnectionInterface::kIceConnectionFailed;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionFailed));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-
-  new_state = webrtc::PeerConnectionInterface::kIceConnectionDisconnected;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionDisconnected));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-
-  new_state = webrtc::PeerConnectionInterface::kIceConnectionClosed;
-  EXPECT_CALL(*mock_client_.get(),
-              DidChangeIceConnectionState(
-                  webrtc::PeerConnectionInterface::kIceConnectionClosed));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
-}
-
 TEST_F(RTCPeerConnectionHandlerTest, OnConnectionChange) {
   testing::InSequence sequence;
 
@@ -1224,55 +1160,6 @@
   EXPECT_EQ("", mock_client_->candidate_sdp());
 }
 
-// TODO(hbos): Enable when not mocking or remove test. https://crbug.com/788659
-TEST_F(RTCPeerConnectionHandlerTest, DISABLED_OnAddAndOnRemoveStream) {
-  rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
-      AddRemoteMockMediaStream("remote_stream", "video", "audio"));
-  // Grab receivers when they're added to/removed from the PC.
-  std::vector<std::unique_ptr<RTCRtpReceiverPlatform>> receivers_added;
-  std::vector<std::unique_ptr<RTCRtpReceiverPlatform>> receivers_removed;
-  EXPECT_CALL(*mock_client_.get(), DidModifyReceiversPlanBForMock(_, _, _))
-      .WillRepeatedly(Invoke(
-          [&receivers_added, &receivers_removed](
-              webrtc::PeerConnectionInterface::SignalingState signaling_state,
-              Vector<std::unique_ptr<RTCRtpReceiverPlatform>>*
-                  platform_receivers_added,
-              Vector<std::unique_ptr<RTCRtpReceiverPlatform>>*
-                  platform_receivers_removed) {
-            if (!platform_receivers_added->IsEmpty()) {
-              receivers_added.push_back(
-                  std::move((*platform_receivers_added)[0]));
-            }
-            if (!platform_receivers_removed->IsEmpty()) {
-              receivers_removed.push_back(
-                  std::move((*platform_receivers_removed)[0]));
-            }
-          }));
-  EXPECT_CALL(
-      *mock_tracker_.Get(),
-      TrackAddTransceiver(
-          pc_handler_.get(),
-          PeerConnectionTracker::TransceiverUpdatedReason::kAddTrack, _, _))
-      .Times(2);
-  EXPECT_CALL(
-      *mock_tracker_.Get(),
-      TrackRemoveTransceiver(
-          pc_handler_.get(),
-          PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack, _, _))
-      .Times(2);
-
-  InvokeOnAddStream(remote_stream);
-  RunMessageLoopsUntilIdle();
-  EXPECT_TRUE(HasReceiverForEveryTrack(remote_stream, receivers_added));
-  InvokeOnRemoveStream(remote_stream);
-  RunMessageLoopsUntilIdle();
-
-  EXPECT_EQ(receivers_added.size(), 2u);
-  EXPECT_EQ(receivers_added.size(), receivers_removed.size());
-  EXPECT_EQ(receivers_added[0]->Id(), receivers_removed[0]->Id());
-  EXPECT_EQ(receivers_added[1]->Id(), receivers_removed[1]->Id());
-}
-
 TEST_F(RTCPeerConnectionHandlerTest, OnIceCandidate) {
   testing::InSequence sequence;
   EXPECT_CALL(*mock_tracker_.Get(),
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index 11e264af..80ec2e4 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -46,358 +46,10 @@
 
 class RTCOfferOptionsPlatform;
 
-namespace {
-
-static const char* kOfferSdpUnifiedPlanSingleAudioSingleVideo =
-    "v=0\r\n"
-    "o=- 6676943034916303038 2 IN IP4 127.0.0.1\r\n"
-    "s=-\r\n"
-    "t=0 0\r\n"
-    "a=group:BUNDLE 0 1\r\n"
-    "a=msid-semantic: WMS\r\n"
-    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
-    "126\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:pKAt\r\n"
-    "a=ice-pwd:bDmIGcCbVl+VkMymNfwdE/Mv\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "F2:D4:95:C5:FC:98:F2:7E:6F:6C:46:BF:5E:05:00:56:4F:A9:BC:4B:1E:56:98:C1:"
-    "68:BF:5E:7D:01:A3:EC:93\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:0\r\n"
-    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
-    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
-    "a=sendrecv\r\n"
-    "a=msid:- 36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtpmap:111 opus/48000/2\r\n"
-    "a=rtcp-fb:111 transport-cc\r\n"
-    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
-    "a=rtpmap:103 ISAC/16000\r\n"
-    "a=rtpmap:104 ISAC/32000\r\n"
-    "a=rtpmap:9 G722/8000\r\n"
-    "a=rtpmap:0 PCMU/8000\r\n"
-    "a=rtpmap:8 PCMA/8000\r\n"
-    "a=rtpmap:106 CN/32000\r\n"
-    "a=rtpmap:105 CN/16000\r\n"
-    "a=rtpmap:13 CN/8000\r\n"
-    "a=rtpmap:110 telephone-event/48000\r\n"
-    "a=rtpmap:112 telephone-event/32000\r\n"
-    "a=rtpmap:113 telephone-event/16000\r\n"
-    "a=rtpmap:126 telephone-event/8000\r\n"
-    "a=ssrc:4264546776 cname:GkUsSfx+DbDplYYT\r\n"
-    "a=ssrc:4264546776 msid: 36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
-    "a=ssrc:4264546776 mslabel:\r\n"
-    "a=ssrc:4264546776 label:36f80301-b634-4c5a-a03b-d1ad79997531\r\n"
-    "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:pKAt\r\n"
-    "a=ice-pwd:bDmIGcCbVl+VkMymNfwdE/Mv\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "F2:D4:95:C5:FC:98:F2:7E:6F:6C:46:BF:5E:05:00:56:4F:A9:BC:4B:1E:56:98:C1:"
-    "68:BF:5E:7D:01:A3:EC:93\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:1\r\n"
-    "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
-    "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
-    "a=extmap:4 urn:3gpp:video-orientation\r\n"
-    "a=extmap:5 "
-    "http://www.ietf.org/id/"
-    "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"
-    "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n"
-    "a=extmap:7 "
-    "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n"
-    "a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n"
-    "a=extmap:10 "
-    "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n"
-    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
-    "a=sendrecv\r\n"
-    "a=msid:- 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtcp-rsize\r\n"
-    "a=rtpmap:96 VP8/90000\r\n"
-    "a=rtcp-fb:96 goog-remb\r\n"
-    "a=rtcp-fb:96 transport-cc\r\n"
-    "a=rtcp-fb:96 ccm fir\r\n"
-    "a=rtcp-fb:96 nack\r\n"
-    "a=rtcp-fb:96 nack pli\r\n"
-    "a=rtpmap:97 rtx/90000\r\n"
-    "a=fmtp:97 apt=96\r\n"
-    "a=rtpmap:98 VP9/90000\r\n"
-    "a=rtcp-fb:98 goog-remb\r\n"
-    "a=rtcp-fb:98 transport-cc\r\n"
-    "a=rtcp-fb:98 ccm fir\r\n"
-    "a=rtcp-fb:98 nack\r\n"
-    "a=rtcp-fb:98 nack pli\r\n"
-    "a=fmtp:98 x-google-profile-id=0\r\n"
-    "a=rtpmap:99 rtx/90000\r\n"
-    "a=fmtp:99 apt=98\r\n"
-    "a=rtpmap:100 red/90000\r\n"
-    "a=rtpmap:101 rtx/90000\r\n"
-    "a=fmtp:101 apt=100\r\n"
-    "a=rtpmap:102 ulpfec/90000\r\n"
-    "a=ssrc-group:FID 680673332 1566706172\r\n"
-    "a=ssrc:680673332 cname:GkUsSfx+DbDplYYT\r\n"
-    "a=ssrc:680673332 msid: 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
-    "a=ssrc:680673332 mslabel:\r\n"
-    "a=ssrc:680673332 label:0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
-    "a=ssrc:1566706172 cname:GkUsSfx+DbDplYYT\r\n"
-    "a=ssrc:1566706172 msid: 0db71b61-c1ae-4741-bcce-320a254244f3\r\n"
-    "a=ssrc:1566706172 mslabel:\r\n"
-    "a=ssrc:1566706172 label:0db71b61-c1ae-4741-bcce-320a254244f3\r\n";
-
-static const char* kOfferSdpUnifiedPlanMultipleAudioTracks =
-    "v=0\r\n"
-    "o=- 1821816752660535838 2 IN IP4 127.0.0.1\r\n"
-    "s=-\r\n"
-    "t=0 0\r\n"
-    "a=group:BUNDLE 0 1\r\n"
-    "a=msid-semantic: WMS\r\n"
-    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
-    "126\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:rbEc\r\n"
-    "a=ice-pwd:vmDec3+MrTigDESzNiDuWBnD\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "05:9B:0A:BC:B3:E1:B9:5C:A6:78:96:23:00:0F:96:71:7B:B0:3E:37:87:1D:3A:62:"
-    "5E:00:A5:27:22:BB:26:5D\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:0\r\n"
-    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
-    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
-    "a=sendrecv\r\n"
-    "a=msid:- adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtpmap:111 opus/48000/2\r\n"
-    "a=rtcp-fb:111 transport-cc\r\n"
-    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
-    "a=rtpmap:103 ISAC/16000\r\n"
-    "a=rtpmap:104 ISAC/32000\r\n"
-    "a=rtpmap:9 G722/8000\r\n"
-    "a=rtpmap:0 PCMU/8000\r\n"
-    "a=rtpmap:8 PCMA/8000\r\n"
-    "a=rtpmap:106 CN/32000\r\n"
-    "a=rtpmap:105 CN/16000\r\n"
-    "a=rtpmap:13 CN/8000\r\n"
-    "a=rtpmap:110 telephone-event/48000\r\n"
-    "a=rtpmap:112 telephone-event/32000\r\n"
-    "a=rtpmap:113 telephone-event/16000\r\n"
-    "a=rtpmap:126 telephone-event/8000\r\n"
-    "a=ssrc:2988156579 cname:gr88KGUzymBvrIaJ\r\n"
-    "a=ssrc:2988156579 msid: adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
-    "a=ssrc:2988156579 mslabel:\r\n"
-    "a=ssrc:2988156579 label:adcd8158-3ad7-4a1f-ac87-8711db959fe8\r\n"
-    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
-    "126\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:rbEc\r\n"
-    "a=ice-pwd:vmDec3+MrTigDESzNiDuWBnD\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "05:9B:0A:BC:B3:E1:B9:5C:A6:78:96:23:00:0F:96:71:7B:B0:3E:37:87:1D:3A:62:"
-    "5E:00:A5:27:22:BB:26:5D\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:1\r\n"
-    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
-    "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
-    "a=sendrecv\r\n"
-    "a=msid:- b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtpmap:111 opus/48000/2\r\n"
-    "a=rtcp-fb:111 transport-cc\r\n"
-    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
-    "a=rtpmap:103 ISAC/16000\r\n"
-    "a=rtpmap:104 ISAC/32000\r\n"
-    "a=rtpmap:9 G722/8000\r\n"
-    "a=rtpmap:0 PCMU/8000\r\n"
-    "a=rtpmap:8 PCMA/8000\r\n"
-    "a=rtpmap:106 CN/32000\r\n"
-    "a=rtpmap:105 CN/16000\r\n"
-    "a=rtpmap:13 CN/8000\r\n"
-    "a=rtpmap:110 telephone-event/48000\r\n"
-    "a=rtpmap:112 telephone-event/32000\r\n"
-    "a=rtpmap:113 telephone-event/16000\r\n"
-    "a=rtpmap:126 telephone-event/8000\r\n"
-    "a=ssrc:2562757057 cname:gr88KGUzymBvrIaJ\r\n"
-    "a=ssrc:2562757057 msid: b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n"
-    "a=ssrc:2562757057 mslabel:\r\n"
-    "a=ssrc:2562757057 label:b5f69d2c-e753-4eb5-a302-d41ee75f9fcb\r\n";
-
-static const char* kOfferSdpPlanBSingleAudioSingleVideo =
-    "v=0\r\n"
-    "o=- 267029810971159627 2 IN IP4 127.0.0.1\r\n"
-    "s=-\r\n"
-    "t=0 0\r\n"
-    "a=group:BUNDLE audio video\r\n"
-    "a=msid-semantic: WMS 655e92b8-9130-44d8-a188-f5f4633d1a8d "
-    "b15218e5-f921-4988-9e1f-6e50ecbd24c2\r\n"
-    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
-    "126\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:ErlQ\r\n"
-    "a=ice-pwd:VCnwY8XlD9EX4gpcOHRhU0HV\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "AC:30:90:F9:3B:CB:9A:0D:C6:FB:F3:D6:D6:97:4F:40:A2:B9:5E:4D:F5:32:DC:A7:"
-    "B0:3A:33:82:C8:67:FF:7A\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:audio\r\n"
-    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
-    "a=sendrecv\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtpmap:111 opus/48000/2\r\n"
-    "a=rtcp-fb:111 transport-cc\r\n"
-    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
-    "a=rtpmap:103 ISAC/16000\r\n"
-    "a=rtpmap:104 ISAC/32000\r\n"
-    "a=rtpmap:9 G722/8000\r\n"
-    "a=rtpmap:0 PCMU/8000\r\n"
-    "a=rtpmap:8 PCMA/8000\r\n"
-    "a=rtpmap:106 CN/32000\r\n"
-    "a=rtpmap:105 CN/16000\r\n"
-    "a=rtpmap:13 CN/8000\r\n"
-    "a=rtpmap:110 telephone-event/48000\r\n"
-    "a=rtpmap:112 telephone-event/32000\r\n"
-    "a=rtpmap:113 telephone-event/16000\r\n"
-    "a=rtpmap:126 telephone-event/8000\r\n"
-    "a=ssrc:1670492497 cname:rNEKgm1NFupmwR4x\r\n"
-    "a=ssrc:1670492497 msid:b15218e5-f921-4988-9e1f-6e50ecbd24c2 "
-    "089fd06c-73e4-4720-a6dc-e182eeaeced7\r\n"
-    "a=ssrc:1670492497 mslabel:b15218e5-f921-4988-9e1f-6e50ecbd24c2\r\n"
-    "a=ssrc:1670492497 label:089fd06c-73e4-4720-a6dc-e182eeaeced7\r\n"
-    "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:ErlQ\r\n"
-    "a=ice-pwd:VCnwY8XlD9EX4gpcOHRhU0HV\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "AC:30:90:F9:3B:CB:9A:0D:C6:FB:F3:D6:D6:97:4F:40:A2:B9:5E:4D:F5:32:DC:A7:"
-    "B0:3A:33:82:C8:67:FF:7A\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:video\r\n"
-    "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
-    "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"
-    "a=extmap:4 urn:3gpp:video-orientation\r\n"
-    "a=extmap:5 "
-    "http://www.ietf.org/id/"
-    "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n"
-    "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n"
-    "a=extmap:7 "
-    "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n"
-    "a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n"
-    "a=extmap:10 "
-    "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n"
-    "a=sendrecv\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtcp-rsize\r\n"
-    "a=rtpmap:96 VP8/90000\r\n"
-    "a=rtcp-fb:96 goog-remb\r\n"
-    "a=rtcp-fb:96 transport-cc\r\n"
-    "a=rtcp-fb:96 ccm fir\r\n"
-    "a=rtcp-fb:96 nack\r\n"
-    "a=rtcp-fb:96 nack pli\r\n"
-    "a=rtpmap:97 rtx/90000\r\n"
-    "a=fmtp:97 apt=96\r\n"
-    "a=rtpmap:98 VP9/90000\r\n"
-    "a=rtcp-fb:98 goog-remb\r\n"
-    "a=rtcp-fb:98 transport-cc\r\n"
-    "a=rtcp-fb:98 ccm fir\r\n"
-    "a=rtcp-fb:98 nack\r\n"
-    "a=rtcp-fb:98 nack pli\r\n"
-    "a=fmtp:98 x-google-profile-id=0\r\n"
-    "a=rtpmap:99 rtx/90000\r\n"
-    "a=fmtp:99 apt=98\r\n"
-    "a=rtpmap:100 red/90000\r\n"
-    "a=rtpmap:101 rtx/90000\r\n"
-    "a=fmtp:101 apt=100\r\n"
-    "a=rtpmap:102 ulpfec/90000\r\n"
-    "a=ssrc-group:FID 3263949794 2166305097\r\n"
-    "a=ssrc:3263949794 cname:rNEKgm1NFupmwR4x\r\n"
-    "a=ssrc:3263949794 msid:655e92b8-9130-44d8-a188-f5f4633d1a8d "
-    "6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
-    "a=ssrc:3263949794 mslabel:655e92b8-9130-44d8-a188-f5f4633d1a8d\r\n"
-    "a=ssrc:3263949794 label:6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
-    "a=ssrc:2166305097 cname:rNEKgm1NFupmwR4x\r\n"
-    "a=ssrc:2166305097 msid:655e92b8-9130-44d8-a188-f5f4633d1a8d "
-    "6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n"
-    "a=ssrc:2166305097 mslabel:655e92b8-9130-44d8-a188-f5f4633d1a8d\r\n"
-    "a=ssrc:2166305097 label:6391e0e8-ac1e-42c2-844c-a7299758db6a\r\n";
-
-static const char* kOfferSdpPlanBMultipleAudioTracks =
-    "v=0\r\n"
-    "o=- 6228437149521864740 2 IN IP4 127.0.0.1\r\n"
-    "s=-\r\n"
-    "t=0 0\r\n"
-    "a=group:BUNDLE audio\r\n"
-    "a=msid-semantic: WMS 46f8615e-7599-49f3-9a45-3cf0faf58614 "
-    "e01b7c23-2b77-4e09-bee7-4b9140e49647\r\n"
-    "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 "
-    "126\r\n"
-    "c=IN IP4 0.0.0.0\r\n"
-    "a=rtcp:9 IN IP4 0.0.0.0\r\n"
-    "a=ice-ufrag:Nzla\r\n"
-    "a=ice-pwd:PL1APGM2pr773UoUOsj8jzBI\r\n"
-    "a=ice-options:trickle\r\n"
-    "a=fingerprint:sha-256 "
-    "DF:8F:89:33:68:AB:55:26:4E:81:CF:95:8C:71:B7:89:45:E7:05:7A:5D:A8:CF:BF:"
-    "60:AA:C7:42:F2:85:23:1D\r\n"
-    "a=setup:actpass\r\n"
-    "a=mid:audio\r\n"
-    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
-    "a=sendrecv\r\n"
-    "a=rtcp-mux\r\n"
-    "a=rtpmap:111 opus/48000/2\r\n"
-    "a=rtcp-fb:111 transport-cc\r\n"
-    "a=fmtp:111 minptime=10;useinbandfec=1\r\n"
-    "a=rtpmap:103 ISAC/16000\r\n"
-    "a=rtpmap:104 ISAC/32000\r\n"
-    "a=rtpmap:9 G722/8000\r\n"
-    "a=rtpmap:0 PCMU/8000\r\n"
-    "a=rtpmap:8 PCMA/8000\r\n"
-    "a=rtpmap:106 CN/32000\r\n"
-    "a=rtpmap:105 CN/16000\r\n"
-    "a=rtpmap:13 CN/8000\r\n"
-    "a=rtpmap:110 telephone-event/48000\r\n"
-    "a=rtpmap:112 telephone-event/32000\r\n"
-    "a=rtpmap:113 telephone-event/16000\r\n"
-    "a=rtpmap:126 telephone-event/8000\r\n"
-    "a=ssrc:2716812081 cname:0QgfsHYGSuZjeg5/\r\n"
-    "a=ssrc:2716812081 msid:e01b7c23-2b77-4e09-bee7-4b9140e49647 "
-    "d73d8a47-3d3f-408f-a2ce-2270eb44ffc5\r\n"
-    "a=ssrc:2716812081 mslabel:e01b7c23-2b77-4e09-bee7-4b9140e49647\r\n"
-    "a=ssrc:2716812081 label:d73d8a47-3d3f-408f-a2ce-2270eb44ffc5\r\n"
-    "a=ssrc:4092260337 cname:0QgfsHYGSuZjeg5/\r\n"
-    "a=ssrc:4092260337 msid:46f8615e-7599-49f3-9a45-3cf0faf58614 "
-    "6b5f436e-f85d-40a1-83e4-acec63ca4b82\r\n"
-    "a=ssrc:4092260337 mslabel:46f8615e-7599-49f3-9a45-3cf0faf58614\r\n"
-    "a=ssrc:4092260337 label:6b5f436e-f85d-40a1-83e4-acec63ca4b82\r\n";
-
-RTCSessionDescriptionInit* CreateSdp(String type, String sdp) {
-  auto* sdp_init = RTCSessionDescriptionInit::Create();
-  sdp_init->setType(type);
-  sdp_init->setSdp(sdp);
-  return sdp_init;
-}
-
-}  // namespace
-
 class RTCPeerConnectionTest : public testing::Test {
  public:
-  RTCPeerConnection* CreatePC(
-      V8TestingScope& scope,
-      const absl::optional<String>& sdp_semantics = absl::nullopt) {
+  RTCPeerConnection* CreatePC(V8TestingScope& scope) {
     RTCConfiguration* config = RTCConfiguration::Create();
-    if (sdp_semantics)
-      config->setSdpSemantics(sdp_semantics.value());
     RTCIceServer* ice_server = RTCIceServer::Create();
     ice_server->setUrl("stun:fake.stun.url");
     HeapVector<Member<RTCIceServer>> ice_servers;
@@ -536,9 +188,9 @@
     EXPECT_TRUE(pc->GetTrackForTesting(track_component));
 
     RemoveStream(scope, pc, stream);
-    // In Unified Plan, transceivers will still reference the stream even after
-    // it is "removed". To make the GC tests work, clear the stream from tracks
-    // so that the stream does not keep tracks alive.
+    // Transceivers will still reference the stream even after it is "removed".
+    // To make the GC tests work, clear the stream from tracks so that the
+    // stream does not keep tracks alive.
     while (!stream->getTracks().IsEmpty())
       stream->removeTrack(stream->getTracks()[0], scope.GetExceptionState());
   }
@@ -573,9 +225,9 @@
     EXPECT_TRUE(pc->GetTrackForTesting(track_component.Get()));
 
     RemoveStream(scope, pc, stream);
-    // In Unified Plan, transceivers will still reference the stream even after
-    // it is "removed". To make the GC tests work, clear the stream from tracks
-    // so that the stream does not keep tracks alive.
+    // Transceivers will still reference the stream even after it is "removed".
+    // To make the GC tests work, clear the stream from tracks so that the
+    // stream does not keep tracks alive.
     while (!stream->getTracks().IsEmpty())
       stream->removeTrack(stream->getTracks()[0], scope.GetExceptionState());
   }
@@ -587,64 +239,6 @@
   EXPECT_FALSE(pc->GetTrackForTesting(track_component.Get()));
 }
 
-TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsUnifiedPlan) {
-  V8TestingScope scope;
-  Persistent<RTCPeerConnection> pc = CreatePC(scope, "unified-plan");
-  RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create();
-  sdp->setType("offer");
-  sdp->setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
-  ASSERT_TRUE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  ASSERT_EQ(pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)),
-            ComplexSdpCategory::kUnifiedPlanExplicitSemantics);
-  sdp->setSdp(kOfferSdpPlanBMultipleAudioTracks);
-  ASSERT_TRUE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  ASSERT_EQ(pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)),
-            ComplexSdpCategory::kPlanBExplicitSemantics);
-  sdp->setSdp("invalid sdp");
-  ASSERT_TRUE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  ASSERT_EQ(pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)),
-            ComplexSdpCategory::kErrorExplicitSemantics);
-  // No Complex SDP is detected if only a single track per m= section is used.
-  sdp->setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
-  ASSERT_FALSE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  sdp->setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
-  ASSERT_FALSE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-}
-
-TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsUnspecified) {
-  V8TestingScope scope;
-  Persistent<RTCPeerConnection> pc = CreatePC(scope);
-  RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create();
-  sdp->setType("offer");
-  sdp->setSdp(kOfferSdpPlanBMultipleAudioTracks);
-  ASSERT_TRUE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  ASSERT_EQ(pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)),
-            ComplexSdpCategory::kPlanBImplicitSemantics);
-  sdp->setSdp(kOfferSdpUnifiedPlanMultipleAudioTracks);
-  ASSERT_TRUE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  ASSERT_EQ(pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)),
-            ComplexSdpCategory::kUnifiedPlanImplicitSemantics);
-  sdp->setSdp("invalid sdp");
-  ASSERT_TRUE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  ASSERT_EQ(pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)),
-            ComplexSdpCategory::kErrorImplicitSemantics);
-  // No Complex SDP is detected if only a single track per m= section is used.
-  sdp->setSdp(kOfferSdpUnifiedPlanSingleAudioSingleVideo);
-  ASSERT_FALSE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-  sdp->setSdp(kOfferSdpPlanBSingleAudioSingleVideo);
-  ASSERT_FALSE(
-      pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
-}
-
 enum class AsyncOperationAction {
   kLeavePending,
   kResolve,
@@ -731,65 +325,6 @@
       AsyncOperationAction::kLeavePending;
 };
 
-TEST_F(RTCPeerConnectionTest, SdpSemanticsUseCounters) {
-  // Constructor with default sdpSemantics (= Unified Plan).
-  {
-    V8TestingScope scope;
-    RTCPeerConnection* pc = CreatePC(scope, /*sdp_semantics=*/absl::nullopt);
-    // Use counters reflect the constructor's sdpSemantics.
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionConstructedWithPlanB));
-    EXPECT_TRUE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionConstructedWithUnifiedPlan));
-    // Setting simple Unified Plan SDP does not affect use counters.
-    pc->setRemoteDescription(
-        scope.GetScriptState(),
-        CreateSdp("offer", kOfferSdpUnifiedPlanSingleAudioSingleVideo),
-        scope.GetExceptionState());
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexPlanB));
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan));
-    // Setting complex Unified Plan SDP does affect use counters.
-    pc->setRemoteDescription(
-        scope.GetScriptState(),
-        CreateSdp("offer", kOfferSdpUnifiedPlanMultipleAudioTracks),
-        scope.GetExceptionState());
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexPlanB));
-    EXPECT_TRUE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan));
-  }
-  // Constructor with {sdpSemantics:"unified-plan"}.
-  {
-    V8TestingScope scope;
-    RTCPeerConnection* pc = CreatePC(scope, "unified-plan");
-    // Use counters reflect the constructor's sdpSemantics.
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionConstructedWithPlanB));
-    EXPECT_TRUE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionConstructedWithUnifiedPlan));
-    // Setting simple Unified Plan SDP does not affect use counters.
-    pc->setRemoteDescription(
-        scope.GetScriptState(),
-        CreateSdp("offer", kOfferSdpUnifiedPlanSingleAudioSingleVideo),
-        scope.GetExceptionState());
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexPlanB));
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan));
-    // Setting complex Unified Plan SDP does affect use counters.
-    pc->setRemoteDescription(
-        scope.GetScriptState(),
-        CreateSdp("offer", kOfferSdpUnifiedPlanMultipleAudioTracks),
-        scope.GetExceptionState());
-    EXPECT_FALSE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexPlanB));
-    EXPECT_TRUE(scope.GetDocument().IsUseCounted(
-        WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan));
-  }
-}
-
 TEST_F(RTCPeerConnectionTest, MediaStreamTrackStopsThrottling) {
   V8TestingScope scope;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
index 4efb5379..a66e17d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
@@ -357,68 +357,4 @@
 RTCRtpReceiverImpl::GetEncodedVideoStreamTransformer() const {
   return internal_->GetEncodedVideoStreamTransformer();
 }
-
-RTCRtpReceiverOnlyTransceiver::RTCRtpReceiverOnlyTransceiver(
-    std::unique_ptr<RTCRtpReceiverPlatform> receiver)
-    : receiver_(std::move(receiver)) {
-  DCHECK(receiver_);
-}
-
-RTCRtpReceiverOnlyTransceiver::~RTCRtpReceiverOnlyTransceiver() {}
-
-RTCRtpTransceiverPlatformImplementationType
-RTCRtpReceiverOnlyTransceiver::ImplementationType() const {
-  return RTCRtpTransceiverPlatformImplementationType::kPlanBReceiverOnly;
-}
-
-uintptr_t RTCRtpReceiverOnlyTransceiver::Id() const {
-  NOTIMPLEMENTED();
-  return 0u;
-}
-
-String RTCRtpReceiverOnlyTransceiver::Mid() const {
-  NOTIMPLEMENTED();
-  return String();
-}
-
-std::unique_ptr<blink::RTCRtpSenderPlatform>
-RTCRtpReceiverOnlyTransceiver::Sender() const {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
-std::unique_ptr<RTCRtpReceiverPlatform>
-RTCRtpReceiverOnlyTransceiver::Receiver() const {
-  return receiver_->ShallowCopy();
-}
-
-webrtc::RtpTransceiverDirection RTCRtpReceiverOnlyTransceiver::Direction()
-    const {
-  NOTIMPLEMENTED();
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-webrtc::RTCError RTCRtpReceiverOnlyTransceiver::SetDirection(
-    webrtc::RtpTransceiverDirection direction) {
-  NOTIMPLEMENTED();
-  return webrtc::RTCError::OK();
-}
-
-absl::optional<webrtc::RtpTransceiverDirection>
-RTCRtpReceiverOnlyTransceiver::CurrentDirection() const {
-  NOTIMPLEMENTED();
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-absl::optional<webrtc::RtpTransceiverDirection>
-RTCRtpReceiverOnlyTransceiver::FiredDirection() const {
-  NOTIMPLEMENTED();
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-webrtc::RTCError RTCRtpReceiverOnlyTransceiver::SetCodecPreferences(
-    Vector<webrtc::RtpCodecCapability>) {
-  NOTIMPLEMENTED();
-  return {};
-}
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h
index d8545217..8eb87b1 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.h
@@ -148,33 +148,6 @@
   scoped_refptr<RTCRtpReceiverInternal> internal_;
 };
 
-class MODULES_EXPORT RTCRtpReceiverOnlyTransceiver
-    : public RTCRtpPlanBTransceiverPlatform {
- public:
-  RTCRtpReceiverOnlyTransceiver(
-      std::unique_ptr<RTCRtpReceiverPlatform> receiver);
-  ~RTCRtpReceiverOnlyTransceiver() override;
-
-  RTCRtpTransceiverPlatformImplementationType ImplementationType()
-      const override;
-  uintptr_t Id() const override;
-  String Mid() const override;
-  std::unique_ptr<blink::RTCRtpSenderPlatform> Sender() const override;
-  std::unique_ptr<RTCRtpReceiverPlatform> Receiver() const override;
-  webrtc::RtpTransceiverDirection Direction() const override;
-  webrtc::RTCError SetDirection(
-      webrtc::RtpTransceiverDirection direction) override;
-  absl::optional<webrtc::RtpTransceiverDirection> CurrentDirection()
-      const override;
-  absl::optional<webrtc::RtpTransceiverDirection> FiredDirection()
-      const override;
-  webrtc::RTCError SetCodecPreferences(
-      Vector<webrtc::RtpCodecCapability>) override;
-
- private:
-  std::unique_ptr<RTCRtpReceiverPlatform> receiver_;
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_RTP_RECEIVER_IMPL_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index a37d24b5..95fd5e44 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -647,11 +647,6 @@
         "The RTCPeerConnection's signalingState is 'closed'.");
     return;
   }
-  if (pc_->sdp_semantics() != webrtc::SdpSemantics::kUnifiedPlan) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      kOnlySupportedInUnifiedPlanMessage);
-    return;
-  }
   Vector<String> stream_ids;
   for (auto stream : streams)
     stream_ids.emplace_back(stream->id());
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
index 87c4a41..e6e4f573 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
@@ -105,8 +105,7 @@
 
   Member<RTCPeerConnection> pc_;
   std::unique_ptr<RTCRtpSenderPlatform> sender_;
-  // The spec says that "kind" should be looked up in transceiver, but keeping
-  // a copy here as long as we support Plan B.
+  // TODO(https://crbug.com/1302249): Delete this, it was only used for Plan B!
   String kind_;
   Member<MediaStreamTrack> track_;
   Member<RTCDtlsTransport> transport_;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
index deadbee5..4edc88e5 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.cc
@@ -567,67 +567,4 @@
   return internal_->GetEncodedVideoStreamTransformer();
 }
 
-RTCRtpSenderOnlyTransceiver::RTCRtpSenderOnlyTransceiver(
-    std::unique_ptr<blink::RTCRtpSenderPlatform> sender)
-    : sender_(std::move(sender)) {
-  DCHECK(sender_);
-}
-
-RTCRtpSenderOnlyTransceiver::~RTCRtpSenderOnlyTransceiver() {}
-
-RTCRtpTransceiverPlatformImplementationType
-RTCRtpSenderOnlyTransceiver::ImplementationType() const {
-  return RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly;
-}
-
-uintptr_t RTCRtpSenderOnlyTransceiver::Id() const {
-  NOTIMPLEMENTED();
-  return 0u;
-}
-
-String RTCRtpSenderOnlyTransceiver::Mid() const {
-  NOTIMPLEMENTED();
-  return String();
-}
-
-std::unique_ptr<blink::RTCRtpSenderPlatform>
-RTCRtpSenderOnlyTransceiver::Sender() const {
-  return sender_->ShallowCopy();
-}
-
-std::unique_ptr<RTCRtpReceiverPlatform> RTCRtpSenderOnlyTransceiver::Receiver()
-    const {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
-webrtc::RtpTransceiverDirection RTCRtpSenderOnlyTransceiver::Direction() const {
-  NOTIMPLEMENTED();
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-webrtc::RTCError RTCRtpSenderOnlyTransceiver::SetDirection(
-    webrtc::RtpTransceiverDirection direction) {
-  NOTIMPLEMENTED();
-  return webrtc::RTCError::OK();
-}
-
-absl::optional<webrtc::RtpTransceiverDirection>
-RTCRtpSenderOnlyTransceiver::CurrentDirection() const {
-  NOTIMPLEMENTED();
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-absl::optional<webrtc::RtpTransceiverDirection>
-RTCRtpSenderOnlyTransceiver::FiredDirection() const {
-  NOTIMPLEMENTED();
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-webrtc::RTCError RTCRtpSenderOnlyTransceiver::SetCodecPreferences(
-    Vector<webrtc::RtpCodecCapability>) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h
index 201830b1..13001446 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender_impl.h
@@ -169,33 +169,6 @@
   scoped_refptr<RTCRtpSenderInternal> internal_;
 };
 
-class MODULES_EXPORT RTCRtpSenderOnlyTransceiver
-    : public RTCRtpPlanBTransceiverPlatform {
- public:
-  explicit RTCRtpSenderOnlyTransceiver(
-      std::unique_ptr<blink::RTCRtpSenderPlatform> sender);
-  ~RTCRtpSenderOnlyTransceiver() override;
-
-  RTCRtpTransceiverPlatformImplementationType ImplementationType()
-      const override;
-  uintptr_t Id() const override;
-  String Mid() const override;
-  std::unique_ptr<RTCRtpSenderPlatform> Sender() const override;
-  std::unique_ptr<RTCRtpReceiverPlatform> Receiver() const override;
-  webrtc::RtpTransceiverDirection Direction() const override;
-  webrtc::RTCError SetDirection(
-      webrtc::RtpTransceiverDirection direction) override;
-  absl::optional<webrtc::RtpTransceiverDirection> CurrentDirection()
-      const override;
-  absl::optional<webrtc::RtpTransceiverDirection> FiredDirection()
-      const override;
-  webrtc::RTCError SetCodecPreferences(
-      Vector<webrtc::RtpCodecCapability>) override;
-
- private:
-  std::unique_ptr<blink::RTCRtpSenderPlatform> sender_;
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_RTP_SENDER_IMPL_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
index e01f929..cb10bc5c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.cc
@@ -379,11 +379,6 @@
   internal_->set_state(std::move(transceiver_state), update_mode);
 }
 
-RTCRtpTransceiverPlatformImplementationType
-RTCRtpTransceiverImpl::ImplementationType() const {
-  return RTCRtpTransceiverPlatformImplementationType::kFullTransceiver;
-}
-
 uintptr_t RTCRtpTransceiverImpl::Id() const {
   return GetId(internal_->state().webrtc_transceiver().get());
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h
index 5ba336a4..4e1baeea 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h
@@ -168,8 +168,6 @@
   blink::RTCRtpSenderImpl* content_sender();
   blink::RTCRtpReceiverImpl* content_receiver();
 
-  RTCRtpTransceiverPlatformImplementationType ImplementationType()
-      const override;
   uintptr_t Id() const override;
   String Mid() const override;
   void SetMid(absl::optional<String>) override;
diff --git a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc
index b1a5197d..83ce2b75 100644
--- a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc
+++ b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.cc
@@ -155,162 +155,4 @@
   return std::move(transceiver_states_);
 }
 
-SurfaceSenderStateOnly::SurfaceSenderStateOnly(
-    rtc::scoped_refptr<webrtc::RtpSenderInterface> sender)
-    : sender_(std::move(sender)) {
-  DCHECK(sender_);
-}
-
-SurfaceSenderStateOnly::~SurfaceSenderStateOnly() = default;
-
-cricket::MediaType SurfaceSenderStateOnly::media_type() const {
-  return sender_->media_type();
-}
-
-absl::optional<std::string> SurfaceSenderStateOnly::mid() const {
-  return absl::nullopt;
-}
-
-rtc::scoped_refptr<webrtc::RtpSenderInterface> SurfaceSenderStateOnly::sender()
-    const {
-  return sender_;
-}
-
-rtc::scoped_refptr<webrtc::RtpReceiverInterface>
-SurfaceSenderStateOnly::receiver() const {
-  return nullptr;
-}
-
-bool SurfaceSenderStateOnly::stopped() const {
-  return false;
-}
-
-bool SurfaceSenderStateOnly::stopping() const {
-  return false;
-}
-
-webrtc::RtpTransceiverDirection SurfaceSenderStateOnly::direction() const {
-  return webrtc::RtpTransceiverDirection::kSendOnly;
-}
-
-void SurfaceSenderStateOnly::SetDirection(
-    webrtc::RtpTransceiverDirection new_direction) {
-  NOTIMPLEMENTED();
-}
-
-absl::optional<webrtc::RtpTransceiverDirection>
-SurfaceSenderStateOnly::current_direction() const {
-  return absl::nullopt;
-}
-
-void SurfaceSenderStateOnly::Stop() {
-  NOTIMPLEMENTED();
-}
-
-webrtc::RTCError SurfaceSenderStateOnly::SetCodecPreferences(
-    rtc::ArrayView<webrtc::RtpCodecCapability>) {
-  RTC_DCHECK_NOTREACHED() << "Not implemented";
-  return {};
-}
-
-std::vector<webrtc::RtpCodecCapability>
-SurfaceSenderStateOnly::codec_preferences() const {
-  return {};
-}
-
-std::vector<webrtc::RtpHeaderExtensionCapability>
-SurfaceSenderStateOnly::HeaderExtensionsToOffer() const {
-  return {};
-}
-
-webrtc::RTCError SurfaceSenderStateOnly::SetOfferedRtpHeaderExtensions(
-    rtc::ArrayView<const webrtc::RtpHeaderExtensionCapability>
-        header_extensions_to_offer) {
-  return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
-}
-
-std::vector<webrtc::RtpHeaderExtensionCapability>
-SurfaceSenderStateOnly::HeaderExtensionsNegotiated() const {
-  return {};
-}
-
-SurfaceReceiverStateOnly::SurfaceReceiverStateOnly(
-    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver)
-    : receiver_(std::move(receiver)) {
-  DCHECK(receiver_);
-}
-
-SurfaceReceiverStateOnly::~SurfaceReceiverStateOnly() {}
-
-cricket::MediaType SurfaceReceiverStateOnly::media_type() const {
-  return receiver_->media_type();
-}
-
-absl::optional<std::string> SurfaceReceiverStateOnly::mid() const {
-  return absl::nullopt;
-}
-
-rtc::scoped_refptr<webrtc::RtpSenderInterface>
-SurfaceReceiverStateOnly::sender() const {
-  return nullptr;
-}
-
-rtc::scoped_refptr<webrtc::RtpReceiverInterface>
-SurfaceReceiverStateOnly::receiver() const {
-  return receiver_;
-}
-
-bool SurfaceReceiverStateOnly::stopped() const {
-  return false;
-}
-
-bool SurfaceReceiverStateOnly::stopping() const {
-  return false;
-}
-
-webrtc::RtpTransceiverDirection SurfaceReceiverStateOnly::direction() const {
-  return webrtc::RtpTransceiverDirection::kRecvOnly;
-}
-
-void SurfaceReceiverStateOnly::SetDirection(
-    webrtc::RtpTransceiverDirection new_direction) {
-  NOTIMPLEMENTED();
-}
-
-absl::optional<webrtc::RtpTransceiverDirection>
-SurfaceReceiverStateOnly::current_direction() const {
-  return absl::nullopt;
-}
-
-void SurfaceReceiverStateOnly::Stop() {
-  NOTIMPLEMENTED();
-}
-
-webrtc::RTCError SurfaceReceiverStateOnly::SetCodecPreferences(
-    rtc::ArrayView<webrtc::RtpCodecCapability>) {
-  RTC_DCHECK_NOTREACHED() << "Not implemented";
-  return {};
-}
-
-std::vector<webrtc::RtpCodecCapability>
-SurfaceReceiverStateOnly::codec_preferences() const {
-  return {};
-}
-
-std::vector<webrtc::RtpHeaderExtensionCapability>
-SurfaceReceiverStateOnly::HeaderExtensionsToOffer() const {
-  return {};
-}
-
-webrtc::RTCError SurfaceReceiverStateOnly::SetOfferedRtpHeaderExtensions(
-    rtc::ArrayView<const webrtc::RtpHeaderExtensionCapability>
-        header_extensions_to_offer) {
-  return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
-}
-
-std::vector<webrtc::RtpHeaderExtensionCapability>
-SurfaceReceiverStateOnly::HeaderExtensionsNegotiated() const {
-  return {};
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.h b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.h
index 2050efe..ea059f1 100644
--- a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.h
+++ b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer.h
@@ -60,78 +60,6 @@
   std::vector<blink::RtpTransceiverState> transceiver_states_;
 };
 
-// A dummy implementation of a transceiver used to surface sender state
-// information only. It is not thread safe, only designed to be passed on to
-// TransceiverStateSurfacer::Initialize().
-class MODULES_EXPORT SurfaceSenderStateOnly
-    : public rtc::RefCountedObject<webrtc::RtpTransceiverInterface> {
- public:
-  explicit SurfaceSenderStateOnly(
-      rtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
-  ~SurfaceSenderStateOnly() override;
-
-  cricket::MediaType media_type() const override;
-  absl::optional<std::string> mid() const override;
-  rtc::scoped_refptr<webrtc::RtpSenderInterface> sender() const override;
-  rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver() const override;
-  bool stopped() const override;
-  webrtc::RtpTransceiverDirection direction() const override;
-  void SetDirection(webrtc::RtpTransceiverDirection new_direction) override;
-  absl::optional<webrtc::RtpTransceiverDirection> current_direction()
-      const override;
-  void Stop() override;
-  bool stopping() const override;
-  webrtc::RTCError SetCodecPreferences(
-      rtc::ArrayView<webrtc::RtpCodecCapability> codecs) override;
-  std::vector<webrtc::RtpCodecCapability> codec_preferences() const override;
-  std::vector<webrtc::RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
-      const override;
-  std::vector<webrtc::RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
-      const override;
-  webrtc::RTCError SetOfferedRtpHeaderExtensions(
-      rtc::ArrayView<const webrtc::RtpHeaderExtensionCapability>
-          header_extensions_to_offer) override;
-
- private:
-  rtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
-};
-
-// A dummy implementation of a transceiver used to surface receiver state
-// information only. It is not thread safe, only designed to be passed on to
-// TransceiverStateSurfacer::Initialize().
-class MODULES_EXPORT SurfaceReceiverStateOnly
-    : public rtc::RefCountedObject<webrtc::RtpTransceiverInterface> {
- public:
-  explicit SurfaceReceiverStateOnly(
-      rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver);
-  ~SurfaceReceiverStateOnly() override;
-
-  cricket::MediaType media_type() const override;
-  absl::optional<std::string> mid() const override;
-  rtc::scoped_refptr<webrtc::RtpSenderInterface> sender() const override;
-  rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver() const override;
-  bool stopped() const override;
-  webrtc::RtpTransceiverDirection direction() const override;
-  void SetDirection(webrtc::RtpTransceiverDirection new_direction) override;
-  absl::optional<webrtc::RtpTransceiverDirection> current_direction()
-      const override;
-  void Stop() override;
-  bool stopping() const override;
-  webrtc::RTCError SetCodecPreferences(
-      rtc::ArrayView<webrtc::RtpCodecCapability> codecs) override;
-  std::vector<webrtc::RtpCodecCapability> codec_preferences() const override;
-  std::vector<webrtc::RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
-      const override;
-  std::vector<webrtc::RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
-      const override;
-  webrtc::RTCError SetOfferedRtpHeaderExtensions(
-      rtc::ArrayView<const webrtc::RtpHeaderExtensionCapability>
-          header_extensions_to_offer) override;
-
- private:
-  rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_TRANSCEIVER_STATE_SURFACER_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
index 11f11b4..97bf43b 100644
--- a/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/transceiver_state_surfacer_test.cc
@@ -322,49 +322,6 @@
   run_loop->Run();
 }
 
-TEST_F(TransceiverStateSurfacerTest, SurfaceSenderStateOnly) {
-  auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
-  auto webrtc_sender =
-      CreateWebRtcSender(local_track_adapter->webrtc_track(), "local_stream");
-  auto waitable_event = AsyncInitializeSurfacerWithWaitableEvent(
-      {rtc::scoped_refptr<webrtc::RtpTransceiverInterface>(
-          new SurfaceSenderStateOnly(webrtc_sender))});
-  waitable_event->Wait();
-  auto transceiver_states = surfacer_->ObtainStates();
-  EXPECT_EQ(1u, transceiver_states.size());
-  auto& transceiver_state = transceiver_states[0];
-  EXPECT_TRUE(transceiver_state.sender_state());
-  EXPECT_TRUE(transceiver_state.sender_state()->is_initialized());
-  EXPECT_FALSE(transceiver_state.receiver_state());
-  // Expect transceiver members be be missing for optional members and have
-  // sensible values for non-optional members.
-  EXPECT_FALSE(transceiver_state.mid());
-  EXPECT_EQ(transceiver_state.direction(),
-            webrtc::RtpTransceiverDirection::kSendOnly);
-  EXPECT_FALSE(transceiver_state.current_direction());
-}
-
-TEST_F(TransceiverStateSurfacerTest, SurfaceReceiverStateOnly) {
-  auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
-  auto webrtc_receiver = CreateWebRtcReceiver("remote_track", "remote_stream");
-  auto waitable_event = AsyncInitializeSurfacerWithWaitableEvent(
-      {rtc::scoped_refptr<webrtc::RtpTransceiverInterface>(
-          new SurfaceReceiverStateOnly(webrtc_receiver))});
-  waitable_event->Wait();
-  auto transceiver_states = surfacer_->ObtainStates();
-  EXPECT_EQ(1u, transceiver_states.size());
-  auto& transceiver_state = transceiver_states[0];
-  EXPECT_FALSE(transceiver_state.sender_state());
-  EXPECT_TRUE(transceiver_state.receiver_state());
-  EXPECT_TRUE(transceiver_state.receiver_state()->is_initialized());
-  // Expect transceiver members be be missing for optional members and have
-  // sensible values for non-optional members.
-  EXPECT_FALSE(transceiver_state.mid());
-  EXPECT_EQ(transceiver_state.direction(),
-            webrtc::RtpTransceiverDirection::kRecvOnly);
-  EXPECT_FALSE(transceiver_state.current_direction());
-}
-
 TEST_F(TransceiverStateSurfacerTest, SurfaceTransceiverWithSctpTransport) {
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto webrtc_transceiver = CreateWebRtcTransceiver(
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.cc
index 29a8425b..0b4be23 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.cc
@@ -54,14 +54,12 @@
         scoped_refptr<webrtc::PeerConnectionInterface> pc,
         scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap>
             track_adapter_map,
-        scoped_refptr<WebRtcSetDescriptionObserver> observer,
-        bool surface_receivers_only)
+        scoped_refptr<WebRtcSetDescriptionObserver> observer)
     : main_task_runner_(std::move(main_task_runner)),
       signaling_task_runner_(std::move(signaling_task_runner)),
       pc_(std::move(pc)),
       track_adapter_map_(std::move(track_adapter_map)),
-      observer_(std::move(observer)),
-      surface_receivers_only_(surface_receivers_only) {}
+      observer_(std::move(observer)) {}
 
 WebRtcSetDescriptionObserverHandlerImpl::
     ~WebRtcSetDescriptionObserverHandlerImpl() = default;
@@ -72,20 +70,13 @@
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
       receiver_only_transceivers;
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
-  // Only surface transceiver/receiver states if the peer connection is not
-  // closed. If the peer connection is closed, the peer connection handler may
-  // have been destroyed along with any track adapters that
-  // blink::TransceiverStateSurfacer assumes exist. This is treated as a special
-  // case due to https://crbug.com/897251.
+  // Only surface transceiver states if the peer connection is not closed. If
+  // the peer connection is closed, the peer connection handler may have been
+  // destroyed along with any track adapters that TransceiverStateSurfacer
+  // assumes exist. This is treated as a special case due to
+  // https://crbug.com/897251.
   if (pc_->signaling_state() != webrtc::PeerConnectionInterface::kClosed) {
-    if (surface_receivers_only_) {
-      for (const auto& receiver : pc_->GetReceivers()) {
-        transceivers.emplace_back(
-            new blink::SurfaceReceiverStateOnly(receiver));
-      }
-    } else {
-      transceivers = pc_->GetTransceivers();
-    }
+    transceivers = pc_->GetTransceivers();
   }
   blink::TransceiverStateSurfacer transceiver_state_surfacer(
       main_task_runner_, signaling_task_runner_);
@@ -146,12 +137,10 @@
     scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
     scoped_refptr<webrtc::PeerConnectionInterface> pc,
     scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-    scoped_refptr<WebRtcSetDescriptionObserver> observer,
-    bool surface_receivers_only) {
+    scoped_refptr<WebRtcSetDescriptionObserver> observer) {
   return new rtc::RefCountedObject<WebRtcSetLocalDescriptionObserverHandler>(
       std::move(main_task_runner), std::move(signaling_task_runner),
-      std::move(pc), std::move(track_adapter_map), std::move(observer),
-      surface_receivers_only);
+      std::move(pc), std::move(track_adapter_map), std::move(observer));
 }
 
 WebRtcSetLocalDescriptionObserverHandler::
@@ -161,16 +150,14 @@
         scoped_refptr<webrtc::PeerConnectionInterface> pc,
         scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap>
             track_adapter_map,
-        scoped_refptr<WebRtcSetDescriptionObserver> observer,
-        bool surface_receivers_only)
+        scoped_refptr<WebRtcSetDescriptionObserver> observer)
     : handler_impl_(
           base::MakeRefCounted<WebRtcSetDescriptionObserverHandlerImpl>(
               std::move(main_task_runner),
               std::move(signaling_task_runner),
               std::move(pc),
               std::move(track_adapter_map),
-              std::move(observer),
-              surface_receivers_only)) {}
+              std::move(observer))) {}
 
 WebRtcSetLocalDescriptionObserverHandler::
     ~WebRtcSetLocalDescriptionObserverHandler() = default;
@@ -186,12 +173,10 @@
     scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
     scoped_refptr<webrtc::PeerConnectionInterface> pc,
     scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-    scoped_refptr<WebRtcSetDescriptionObserver> observer,
-    bool surface_receivers_only) {
+    scoped_refptr<WebRtcSetDescriptionObserver> observer) {
   return new rtc::RefCountedObject<WebRtcSetRemoteDescriptionObserverHandler>(
       std::move(main_task_runner), std::move(signaling_task_runner),
-      std::move(pc), std::move(track_adapter_map), std::move(observer),
-      surface_receivers_only);
+      std::move(pc), std::move(track_adapter_map), std::move(observer));
 }
 
 WebRtcSetRemoteDescriptionObserverHandler::
@@ -201,16 +186,14 @@
         scoped_refptr<webrtc::PeerConnectionInterface> pc,
         scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap>
             track_adapter_map,
-        scoped_refptr<WebRtcSetDescriptionObserver> observer,
-        bool surface_receivers_only)
+        scoped_refptr<WebRtcSetDescriptionObserver> observer)
     : handler_impl_(
           base::MakeRefCounted<WebRtcSetDescriptionObserverHandlerImpl>(
               std::move(main_task_runner),
               std::move(signaling_task_runner),
               std::move(pc),
               std::move(track_adapter_map),
-              std::move(observer),
-              surface_receivers_only)) {}
+              std::move(observer))) {}
 
 WebRtcSetRemoteDescriptionObserverHandler::
     ~WebRtcSetRemoteDescriptionObserverHandler() = default;
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.h b/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.h
index 16e710b..070343b 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.h
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer.h
@@ -108,8 +108,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::PeerConnectionInterface> pc,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-      scoped_refptr<WebRtcSetDescriptionObserver> observer,
-      bool surface_receivers_only);
+      scoped_refptr<WebRtcSetDescriptionObserver> observer);
 
   WebRtcSetDescriptionObserverHandlerImpl(
       const WebRtcSetDescriptionObserverHandlerImpl&) = delete;
@@ -143,7 +142,6 @@
   scoped_refptr<webrtc::PeerConnectionInterface> pc_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
   scoped_refptr<WebRtcSetDescriptionObserver> observer_;
-  bool surface_receivers_only_;
 };
 
 // An implementation of webrtc::SetLocalDescriptionObserverInterface for
@@ -156,8 +154,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::PeerConnectionInterface> pc,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-      scoped_refptr<WebRtcSetDescriptionObserver> observer,
-      bool surface_receivers_only);
+      scoped_refptr<WebRtcSetDescriptionObserver> observer);
 
   WebRtcSetLocalDescriptionObserverHandler(
       const WebRtcSetLocalDescriptionObserverHandler&) = delete;
@@ -174,8 +171,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::PeerConnectionInterface> pc,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-      scoped_refptr<WebRtcSetDescriptionObserver> observer,
-      bool surface_receivers_only);
+      scoped_refptr<WebRtcSetDescriptionObserver> observer);
   ~WebRtcSetLocalDescriptionObserverHandler() override;
 
   scoped_refptr<WebRtcSetDescriptionObserverHandlerImpl> handler_impl_;
@@ -191,8 +187,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::PeerConnectionInterface> pc,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-      scoped_refptr<WebRtcSetDescriptionObserver> observer,
-      bool surface_receivers_only);
+      scoped_refptr<WebRtcSetDescriptionObserver> observer);
 
   WebRtcSetRemoteDescriptionObserverHandler(
       const WebRtcSetRemoteDescriptionObserverHandler&) = delete;
@@ -210,8 +205,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::PeerConnectionInterface> pc,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-      scoped_refptr<WebRtcSetDescriptionObserver> observer,
-      bool surface_receivers_only);
+      scoped_refptr<WebRtcSetDescriptionObserver> observer);
   ~WebRtcSetRemoteDescriptionObserverHandler() override;
 
   scoped_refptr<WebRtcSetDescriptionObserverHandlerImpl> handler_impl_;
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer_test.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer_test.cc
index 84a06075..a239e60 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_set_description_observer_test.cc
@@ -77,8 +77,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
       scoped_refptr<webrtc::PeerConnectionInterface> pc,
       scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
-      scoped_refptr<WebRtcSetDescriptionObserver> observer,
-      bool surface_receivers_only)
+      scoped_refptr<WebRtcSetDescriptionObserver> observer)
       : signaling_task_runner_(std::move(signaling_task_runner)),
         handler_type_(handler_type),
         local_handler_(nullptr),
@@ -87,14 +86,12 @@
       case ObserverHandlerType::kLocal:
         local_handler_ = WebRtcSetLocalDescriptionObserverHandler::Create(
             std::move(main_task_runner), signaling_task_runner_, std::move(pc),
-            std::move(track_adapter_map), std::move(observer),
-            surface_receivers_only);
+            std::move(track_adapter_map), std::move(observer));
         break;
       case ObserverHandlerType::kRemote:
         remote_handler_ = WebRtcSetRemoteDescriptionObserverHandler::Create(
             std::move(main_task_runner), signaling_task_runner_, std::move(pc),
-            std::move(track_adapter_map), std::move(observer),
-            surface_receivers_only);
+            std::move(track_adapter_map), std::move(observer));
         break;
     }
   }
@@ -171,28 +168,17 @@
   kReceiversOnly,
 };
 
-using TestVariety = std::tuple<ObserverHandlerType, StateSurfacerType>;
-
-struct PrintToStringTestVariety {
+struct PrintToStringObserverHandlerType {
   std::string operator()(
-      const testing::TestParamInfo<TestVariety>& info) const {
-    ObserverHandlerType handler_type = std::get<0>(info.param);
-    StateSurfacerType surfacer_type = std::get<1>(info.param);
+      const testing::TestParamInfo<ObserverHandlerType>& info) const {
+    ObserverHandlerType handler_type = info.param;
     std::string str;
     switch (handler_type) {
       case ObserverHandlerType::kLocal:
-        str += "LocalDescriptionWith";
+        str += "LocalDescription";
         break;
       case ObserverHandlerType::kRemote:
-        str += "RemoteDescriptionWith";
-        break;
-    }
-    switch (surfacer_type) {
-      case StateSurfacerType::kTransceivers:
-        str += "TransceiverStates";
-        break;
-      case StateSurfacerType::kReceiversOnly:
-        str += "ReceiverStates";
+        str += "RemoteDescription";
         break;
     }
     return str;
@@ -205,20 +191,10 @@
 // setLocalDescription() and setRemoteDescription() respectively, are virtually
 // identical in terms of functionality but have different class hierarchies due
 // to webrtc observer interfaces being different classes.
-//
-// Each handler is testable under two modes of operation: surfacing state
-// information about transceivers (includes both senders and receivers), or only
-// surfacing receiver state information. Unified Plan requires the former and
-// Plan B requires the latter.
-//
-// Parameterization allows easily running the same tests for each handler and
-// mode, as specified by the TestVariety.
 class WebRtcSetDescriptionObserverHandlerTest
-    : public ::testing::TestWithParam<TestVariety> {
+    : public ::testing::TestWithParam<ObserverHandlerType> {
  public:
-  WebRtcSetDescriptionObserverHandlerTest()
-      : handler_type_(std::get<0>(GetParam())),
-        surfacer_type_(std::get<1>(GetParam())) {}
+  WebRtcSetDescriptionObserverHandlerTest() : handler_type_(GetParam()) {}
 
   void SetUp() override {
     pc_ = new MockPeerConnectionInterface;
@@ -232,8 +208,7 @@
     observer_handler_ = std::make_unique<ObserverHandlerWrapper>(
         handler_type_, main_thread_,
         dependency_factory_->GetWebRtcSignalingTaskRunner(), pc_,
-        track_adapter_map_, observer_,
-        surfacer_type_ == StateSurfacerType::kReceiversOnly);
+        track_adapter_map_, observer_);
   }
 
   void TearDown() override { blink::WebHeap::CollectAllGarbageForTesting(); }
@@ -253,8 +228,6 @@
   }
 
   void CreateTransceivers() {
-    ASSERT_EQ(StateSurfacerType::kTransceivers, surfacer_type_);
-
     auto* component = CreateLocalTrack("local_track");
     auto local_track_adapter =
         track_adapter_map_->GetOrCreateLocalTrackAdapter(component);
@@ -287,9 +260,7 @@
   }
 
   void ExpectMatchingTransceivers() {
-    ASSERT_EQ(StateSurfacerType::kTransceivers, surfacer_type_);
     ASSERT_EQ(1u, transceivers_.size());
-
     auto transceiver = transceivers_[0];
     auto sender = transceiver->sender();
     auto receiver = transceiver->receiver();
@@ -324,41 +295,6 @@
     EXPECT_EQ(receiver->stream_ids(), receiver_state.stream_ids());
   }
 
-  void CreateReceivers() {
-    ASSERT_EQ(StateSurfacerType::kReceiversOnly, surfacer_type_);
-
-    scoped_refptr<blink::MockWebRtcAudioTrack> remote_track =
-        blink::MockWebRtcAudioTrack::Create("remote_track");
-    rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
-        new rtc::RefCountedObject<blink::MockMediaStream>("remote_stream"));
-    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver(
-        new rtc::RefCountedObject<blink::FakeRtpReceiver>(
-            rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>(
-                remote_track.get()),
-            std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
-                {remote_stream})));
-    receivers_.push_back(receiver);
-    EXPECT_CALL(*pc_, GetReceivers()).WillRepeatedly(Return(receivers_));
-  }
-
-  void ExpectMatchingReceivers() {
-    ASSERT_EQ(StateSurfacerType::kReceiversOnly, surfacer_type_);
-    ASSERT_EQ(1u, receivers_.size());
-
-    auto receiver = receivers_[0];
-    EXPECT_EQ(1u, observer_->states().transceiver_states.size());
-    const blink::RtpTransceiverState& transceiver_state =
-        observer_->states().transceiver_states[0];
-    EXPECT_FALSE(transceiver_state.sender_state());
-    EXPECT_TRUE(transceiver_state.receiver_state());
-    const blink::RtpReceiverState& receiver_state =
-        *transceiver_state.receiver_state();
-    EXPECT_TRUE(receiver_state.is_initialized());
-    EXPECT_EQ(receiver.get(), receiver_state.webrtc_receiver());
-    EXPECT_EQ(receiver->track(), receiver_state.track_ref()->webrtc_track());
-    EXPECT_EQ(receiver->stream_ids(), receiver_state.stream_ids());
-  }
-
  protected:
   scoped_refptr<MockPeerConnectionInterface> pc_;
   Persistent<MockPeerConnectionDependencyFactory> dependency_factory_;
@@ -367,7 +303,6 @@
   scoped_refptr<WebRtcSetDescriptionObserverForTest> observer_;
 
   ObserverHandlerType handler_type_;
-  StateSurfacerType surfacer_type_;
   std::unique_ptr<ObserverHandlerWrapper> observer_handler_;
 
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
@@ -375,20 +310,10 @@
   std::vector<
       std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>>
       local_track_adapters_;
-  // Used instead of |transceivers_| when |surfacer_type_| is
-  // StateSurfacerType::kReceiversOnly.
-  std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> receivers_;
 };
 
 TEST_P(WebRtcSetDescriptionObserverHandlerTest, OnSuccess) {
-  switch (surfacer_type_) {
-    case StateSurfacerType::kTransceivers:
-      CreateTransceivers();
-      break;
-    case StateSurfacerType::kReceiversOnly:
-      CreateReceivers();
-      break;
-  }
+  CreateTransceivers();
 
   EXPECT_CALL(*pc_, signaling_state())
       .WillRepeatedly(Return(webrtc::PeerConnectionInterface::kStable));
@@ -400,25 +325,11 @@
   EXPECT_EQ(webrtc::PeerConnectionInterface::kStable,
             observer_->states().signaling_state);
 
-  switch (surfacer_type_) {
-    case StateSurfacerType::kTransceivers:
-      ExpectMatchingTransceivers();
-      break;
-    case StateSurfacerType::kReceiversOnly:
-      ExpectMatchingReceivers();
-      break;
-  }
+  ExpectMatchingTransceivers();
 }
 
 TEST_P(WebRtcSetDescriptionObserverHandlerTest, OnFailure) {
-  switch (surfacer_type_) {
-    case StateSurfacerType::kTransceivers:
-      CreateTransceivers();
-      break;
-    case StateSurfacerType::kReceiversOnly:
-      CreateReceivers();
-      break;
-  }
+  CreateTransceivers();
 
   EXPECT_CALL(*pc_, signaling_state())
       .WillRepeatedly(Return(webrtc::PeerConnectionInterface::kStable));
@@ -433,14 +344,7 @@
   EXPECT_EQ(webrtc::PeerConnectionInterface::kStable,
             observer_->states().signaling_state);
 
-  switch (surfacer_type_) {
-    case StateSurfacerType::kTransceivers:
-      ExpectMatchingTransceivers();
-      break;
-    case StateSurfacerType::kReceiversOnly:
-      ExpectMatchingReceivers();
-      break;
-  }
+  ExpectMatchingTransceivers();
 }
 
 // Test coverage for https://crbug.com/897251. If the webrtc peer connection is
@@ -453,14 +357,7 @@
 // due to track adapters not existing.
 TEST_P(WebRtcSetDescriptionObserverHandlerTest,
        ClosePeerConnectionBeforeCallback) {
-  switch (surfacer_type_) {
-    case StateSurfacerType::kTransceivers:
-      CreateTransceivers();
-      break;
-    case StateSurfacerType::kReceiversOnly:
-      CreateReceivers();
-      break;
-  }
+  CreateTransceivers();
 
   // Simulate the peer connection having been closed and local track adapters
   // destroyed before the observer was invoked.
@@ -478,17 +375,10 @@
   EXPECT_EQ(0u, observer_->states().transceiver_states.size());
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    WebRtcSetDescriptionObserverHandlerTest,
-    ::testing::Values(std::make_tuple(ObserverHandlerType::kLocal,
-                                      StateSurfacerType::kTransceivers),
-                      std::make_tuple(ObserverHandlerType::kRemote,
-                                      StateSurfacerType::kTransceivers),
-                      std::make_tuple(ObserverHandlerType::kLocal,
-                                      StateSurfacerType::kReceiversOnly),
-                      std::make_tuple(ObserverHandlerType::kRemote,
-                                      StateSurfacerType::kReceiversOnly)),
-    PrintToStringTestVariety());
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebRtcSetDescriptionObserverHandlerTest,
+                         ::testing::Values(ObserverHandlerType::kLocal,
+                                           ObserverHandlerType::kRemote),
+                         PrintToStringObserverHandlerType());
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
index 9fafdd5..861bdf7 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
@@ -166,7 +166,7 @@
     video_bounds = video_element->GetDocument().View()->FrameToViewport(
         ToEnclosingRect(layout_video->LocalToAbsoluteRect(content_rect)));
   } else {
-    video_bounds = video_element->BoundsInViewport();
+    video_bounds = video_element->BoundsInWidget();
   }
 
   picture_in_picture_service_->StartSession(
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
index 1b7c40d..141e92f6 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_test.cc
@@ -429,7 +429,7 @@
       MakeGarbageCollected<HTMLVideoElement>(*document);
   document->body()->AppendChild(video);
 
-  gfx::Point bounds = video->BoundsInViewport().CenterPoint();
+  gfx::Point bounds = video->BoundsInWidget().CenterPoint();
 
   // Performs the specified media player action on the media element at the
   // given location.
@@ -462,7 +462,7 @@
 }
 
 TEST_F(PictureInPictureControllerTest,
-       EnterPictureInPictureProvideSourceBoundsSetToBoundsInViewport) {
+       EnterPictureInPictureProvideSourceBoundsSetToBoundsInWidget) {
   EXPECT_EQ(nullptr, PictureInPictureControllerImpl::From(GetDocument())
                          .PictureInPictureElement());
 
@@ -479,8 +479,8 @@
 
   // We expect that the video element has some nontrivial rect, else this won't
   // really test anything.
-  ASSERT_NE(Video()->BoundsInViewport(), gfx::Rect());
-  EXPECT_EQ(Service().source_bounds(), Video()->BoundsInViewport());
+  ASSERT_NE(Video()->BoundsInWidget(), gfx::Rect());
+  EXPECT_EQ(Service().source_bounds(), Video()->BoundsInWidget());
 }
 
 TEST_F(PictureInPictureControllerTest,
@@ -533,7 +533,7 @@
 
   // Source bounds are expected to match the poster image size, not the bounds
   // of the video element.
-  EXPECT_EQ(Video()->BoundsInViewport(), gfx::Rect(33, 33, 300, 300));
+  EXPECT_EQ(Video()->BoundsInWidget(), gfx::Rect(33, 33, 300, 300));
   EXPECT_EQ(Service().source_bounds(), gfx::Rect(173, 173, 20, 20));
 }
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index a991873..65c6521 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1950,8 +1950,10 @@
 
   if (is_fuchsia) {
     use_cfv1 = false
-    additional_manifest_fragments =
-        [ "//build/config/fuchsia/test/test_fonts.shard.test-cml" ]
+    additional_manifest_fragments = [
+      "//build/config/fuchsia/test/test_fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
+    ]
 
     # Oilpan reuses V8's v8::PageAllocator which generally requires JIT
     # permissions.
@@ -2419,8 +2421,10 @@
     use_cfv1 = false
     deps += [ "//skia:test_fonts_cfv2" ]
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
-    additional_manifest_fragments =
-        [ "//build/config/fuchsia/test/test_fonts.shard.test-cml" ]
+    additional_manifest_fragments = [
+      "//build/config/fuchsia/test/test_fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
+    ]
   } else {
     deps += [ "//skia:test_fonts" ]
   }
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 0597de7..5dc521cc 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -567,10 +567,6 @@
   RuntimeEnabledFeatures::SetGetDisplayMediaEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableSignedExchangeSubresourcePrefetch(bool enable) {
-  RuntimeEnabledFeatures::SetSignedExchangeSubresourcePrefetchEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableSubresourceWebBundles(bool enable) {
   RuntimeEnabledFeatures::SetSubresourceWebBundlesEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index 9ef4d56..39ae5b7 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -515,10 +515,6 @@
   return resource_request_->IsFromOriginDirtyStyleSheet();
 }
 
-bool WebURLRequest::IsSignedExchangePrefetchCacheEnabled() const {
-  return resource_request_->IsSignedExchangePrefetchCacheEnabled();
-}
-
 absl::optional<base::UnguessableToken> WebURLRequest::RecursivePrefetchToken()
     const {
   return resource_request_->RecursivePrefetchToken();
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index 4f0d20d..6141fa4 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -124,8 +124,10 @@
   if (is_fuchsia) {
     use_cfv1 = false
 
-    additional_manifest_fragments =
-        [ "//build/config/fuchsia/test/test_fonts.shard.test-cml" ]
+    additional_manifest_fragments = [
+      "//build/config/fuchsia/test/test_fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
+    ]
 
     # Oilpan reuses V8's v8::PageAllocator which generally requires JIT
     # permissions.
@@ -196,6 +198,8 @@
 
     # Oilpan reuses V8's v8::PageAllocator which generally requires JIT
     # permissions.
+    additional_manifest_fragments =
+        [ "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml" ]
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
   }
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
index be68305..1e1f604 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
@@ -204,10 +204,6 @@
     is_from_origin_dirty_style_sheet_ = dirty;
   }
 
-  void SetSignedExchangePrefetchCacheEnabled(bool enabled) {
-    resource_request_.SetSignedExchangePrefetchCacheEnabled(enabled);
-  }
-
   RenderBlockingBehavior GetRenderBlockingBehavior() const {
     return render_blocking_behavior_;
   }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index cfc33b1..234d5a1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -214,8 +214,6 @@
   request->SetUkmSourceId(GetUkmSourceId());
   request->SetInspectorId(InspectorId());
   request->SetFromOriginDirtyStyleSheet(IsFromOriginDirtyStyleSheet());
-  request->SetSignedExchangePrefetchCacheEnabled(
-      IsSignedExchangePrefetchCacheEnabled());
   request->SetRecursivePrefetchToken(RecursivePrefetchToken());
   request->SetFetchLikeAPI(IsFetchLikeAPI());
   request->SetFavicon(IsFavicon());
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index 957e341..26e8ae0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -478,13 +478,6 @@
     is_from_origin_dirty_style_sheet_ = dirty;
   }
 
-  bool IsSignedExchangePrefetchCacheEnabled() const {
-    return is_signed_exchange_prefetch_cache_enabled_;
-  }
-  void SetSignedExchangePrefetchCacheEnabled(bool enabled) {
-    is_signed_exchange_prefetch_cache_enabled_ = enabled;
-  }
-
   bool IsFetchLikeAPI() const { return is_fetch_like_api_; }
 
   void SetFetchLikeAPI(bool enabled) { is_fetch_like_api_ = enabled; }
@@ -621,8 +614,6 @@
 
   bool is_from_origin_dirty_style_sheet_ = false;
 
-  bool is_signed_exchange_prefetch_cache_enabled_ = false;
-
   bool is_fetch_like_api_ = false;
 
   bool is_favicon_ = false;
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
index f58bf633..1a338b7 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc
@@ -360,12 +360,6 @@
     dest->devtools_stack_id = src.GetDevToolsStackId().value().Ascii();
   }
 
-  if (src.IsSignedExchangePrefetchCacheEnabled()) {
-    DCHECK_EQ(src.GetRequestContext(),
-              mojom::blink::RequestContextType::PREFETCH);
-    dest->is_signed_exchange_prefetch_cache_enabled = true;
-  }
-
   dest->is_fetch_like_api = src.IsFetchLikeAPI();
 
   dest->is_favicon = src.IsFavicon();
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h b/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h
index 9fd2ff7..61cb8f3 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h
@@ -44,7 +44,6 @@
 namespace blink {
 
 class RTCIceCandidatePlatform;
-class RTCRtpReceiverPlatform;
 class RTCRtpTransceiverPlatform;
 class RTCSessionDescriptionPlatform;
 
@@ -75,15 +74,8 @@
       RTCSessionDescriptionPlatform* current_remote_description) = 0;
   virtual void DidChangeIceGatheringState(
       webrtc::PeerConnectionInterface::IceGatheringState) = 0;
-  virtual void DidChangeIceConnectionState(
-      webrtc::PeerConnectionInterface::IceConnectionState) = 0;
   virtual void DidChangePeerConnectionState(
       webrtc::PeerConnectionInterface::PeerConnectionState) {}
-  virtual void DidModifyReceiversPlanB(
-      webrtc::PeerConnectionInterface::SignalingState,
-      Vector<std::unique_ptr<RTCRtpReceiverPlatform>> platform_receivers_added,
-      Vector<std::unique_ptr<RTCRtpReceiverPlatform>>
-          platform_receivers_removed) = 0;
   virtual void DidModifyTransceivers(
       webrtc::PeerConnectionInterface::SignalingState,
       Vector<std::unique_ptr<RTCRtpTransceiverPlatform>>,
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h
index f7ecbc4..cd21a1a 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h
@@ -20,23 +20,6 @@
 class RTCRtpReceiverPlatform;
 class RTCRtpSenderPlatform;
 
-// In Unified Plan transceivers exist and a full implementation of
-// RTCRtpTransceiverPlatform is required.
-// In Plan B, the same methods that would be used to surface transceivers only
-// surface the sender or receiver component.
-// To make the Plan B -> Unified Plan transition easier,
-// RTCRtpTransceiverPlatform is used in both cases and ImplementationType
-// indicates which methods are applicable for the RTCRtpTransceiverPlatform
-// implementation.
-enum class RTCRtpTransceiverPlatformImplementationType {
-  // Unified Plan: All methods implemented.
-  kFullTransceiver,
-  // Plan B: Only Sender() is implemented.
-  kPlanBSenderOnly,
-  // Plan B: Only Receiver() is implemented.
-  kPlanBReceiverOnly,
-};
-
 // Interface for content to implement as to allow the surfacing of transceivers.
 // TODO(hbos): [Onion Soup] Remove the content layer versions of this class and
 // rely on webrtc directly from blink. Requires coordination with senders and
@@ -45,11 +28,6 @@
  public:
   virtual ~RTCRtpTransceiverPlatform();
 
-  // Which methods (other than ImplementationType()) is guaranteed to be
-  // implemented.
-  virtual RTCRtpTransceiverPlatformImplementationType ImplementationType()
-      const = 0;
-
   // Identifies the webrtc-layer transceiver. Multiple RTCRtpTransceiverPlatform
   // can exist for the same webrtc-layer transceiver.
   virtual uintptr_t Id() const = 0;
@@ -79,24 +57,6 @@
       const = 0;
 };
 
-// This class contains dummy implementations for functions that are not
-// supported in Plan B mode.
-class RTCRtpPlanBTransceiverPlatform : public RTCRtpTransceiverPlatform {
- public:
-  webrtc::RTCError SetOfferedRtpHeaderExtensions(
-      Vector<webrtc::RtpHeaderExtensionCapability> header_extensions) override {
-    return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
-  }
-  Vector<webrtc::RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
-      const override {
-    return {};
-  }
-  Vector<webrtc::RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
-      const override {
-    return {};
-  }
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_RTP_TRANSCEIVER_PLATFORM_H_
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 5dc4206..35df48a9e 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1957,17 +1957,6 @@
       name: "RTCSvcScalabilityMode",
       status: "experimental",
     },
-    // Enables the use of |RTCConfiguration::sdpSemantics| to override the
-    // default SDP semantics at RTCPeerConnection construction.
-    {
-      name: "RTCUnifiedPlan",
-      status: "stable",
-    },
-    // Overrides the default SDP semantics to be Unified Plan at
-    // RTCPeerConnection construction (unless otherwise specified).
-    {
-      name: "RTCUnifiedPlanByDefault",
-    },
     {
       name: "SanitizerAPI",
       status: "experimental",
@@ -2103,11 +2092,6 @@
       status: "experimental",
     },
     {
-      name: "SignedExchangeSubresourcePrefetch",
-      origin_trial_feature_name: "SignedExchangeSubresourcePrefetch",
-      status: "experimental",
-    },
-    {
       name: "SiteInitiatedMirroring",
       status: "experimental",
     },
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index ba3c1135..565e974 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1328,7 +1328,6 @@
 
 # No H.264 decoder support in bot - just skip all webrtc tests.
 crbug.com/840659 external/wpt/webrtc/* [ Skip ]
-crbug.com/840659 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/* [ Skip ]
 
 # MathML depends on LayoutNG.
 crbug.com/6606 external/wpt/mathml/* [ Skip ]
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
index 0a288cc6..eb91d15 100644
--- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
+++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -99,7 +99,6 @@
 crbug.com/1126305 virtual/prerender/wpt_internal/prerender/restriction-encrypted-media.https.html [ Skip ]
 crbug.com/1126305 virtual/prerender/wpt_internal/prerender/restriction-media-auto-play-attribute.html [ Skip ]
 crbug.com/1126305 virtual/prerender/wpt_internal/prerender/restriction-media-play.html [ Skip ]
-virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/video-codecs.https.html [ Skip ]
 wpt_internal/mediastream/mediastreamtrackprocessor-transfer-to-worker.html [ Skip ]
 wpt_internal/webcodecs/annexb_decoding.https.any.html [ Skip ]
 wpt_internal/webcodecs/annexb_decoding.https.any.worker.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c4c5f0ce..f34c0ab 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -216,8 +216,6 @@
 crbug.com/1124352 external/wpt/picture-in-picture/picture-in-picture-element.html [ Crash Pass ]
 crbug.com/1124352 external/wpt/picture-in-picture/shadow-dom.html [ Crash Pass ]
 
-crbug.com/1174965 [ Mac ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state.html [ Failure Pass ]
-
 # These tests require portals, and some require cross-origin portals.
 # Keep this in sync with VirtualTestSuites.
 crbug.com/1093466 external/wpt/fetch/metadata/portal.https.sub.html [ Skip ]
@@ -3130,9 +3128,6 @@
 
 crbug.com/1333451 external/wpt/css/css-ruby/intra-base-white-space-001.html [ Failure ]
 
-# WebRTC: Perfect Negotiation times out in Plan B. This is expected.
-crbug.com/980872 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation.https.html [ Skip Timeout ]
-
 # WebRTC: there's an open bug with some capture scenarios not working.
 crbug.com/1156408 external/wpt/webrtc/RTCPeerConnection-relay-canvas.https.html [ Failure Pass Timeout ]
 
@@ -3418,7 +3413,6 @@
 crbug.com/626703 [ Linux ] external/wpt/resource-timing/object-not-found-adds-entry.html [ Timeout ]
 crbug.com/626703 external/wpt/webrtc/RTCConfiguration-iceTransportPolicy.html [ Skip Timeout ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/webrtc/RTCDataChannel-bufferedAmount.html [ Crash ]
-crbug.com/626703 [ Win10.20h2 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-bufferedAmount.html [ Crash ]
 crbug.com/626703 [ Mac11 ] external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html [ Crash ]
 crbug.com/626703 [ Mac11 ] external/wpt/mediacapture-record/MediaRecorder-mimetype.html [ Failure Timeout ]
 crbug.com/626703 [ Mac12 ] external/wpt/mediacapture-record/MediaRecorder-mimetype.html [ Failure Timeout ]
@@ -3434,7 +3428,6 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCDataChannel-close.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCPeerConnection-capture-video.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/navigation-timing/nav2_test_response_end_and_duration_before_during_and_after_load_event.html [ Skip Timeout ]
 crbug.com/626703 [ Mac12-arm64 ] external/wpt/navigation-timing/nav2_test_response_end_and_duration_before_during_and_after_load_event.html [ Skip Timeout ]
 crbug.com/1270841 [ Mac ] external/wpt/media-capabilities/encodingInfo.any.worker.html [ Crash ]
@@ -3442,8 +3435,6 @@
 crbug.com/626703 [ Mac11 ] virtual/scroll-unification-unified-autoplay/external/wpt/feature-policy/feature-policy-header-policy-declined.https.sub.html [ Timeout ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/web-locks/bfcache/abort.tentative.https.html [ Failure Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCPeerConnection-getStats.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/candidate-exchange.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/workers/modules/shared-worker-import-csp.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.html?1-10 [ Skip Timeout ]
@@ -3453,7 +3444,6 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCPeerConnection-ondatachannel.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/protocol/ice-state.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-videoDetectorTest.html [ Skip Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/media-capabilities/encodingInfo.any.worker.html [ Crash ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/media-capabilities/encodingInfo.any.worker.html [ Crash ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/forced-high-contrast-colors/external/wpt/forced-colors-mode/backplate/forced-colors-mode-backplate-01.html [ Crash ]
@@ -3475,7 +3465,6 @@
 crbug.com/626703 external/wpt/geolocation-API/non-fully-active.https.html [ Timeout ]
 crbug.com/626703 external/wpt/selection/textcontrols/selectionchange-bubble.html [ Timeout ]
 crbug.com/626703 external/wpt/density-size-correction/density-corrected-image-svg-aspect-ratio-cross-origin.sub.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/candidate-exchange.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/css/css-align/self-alignment/self-align-safe-unsafe-flex-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-align/self-alignment/self-align-safe-unsafe-flex-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-align/self-alignment/self-align-safe-unsafe-flex-003.html [ Failure ]
@@ -3506,9 +3495,6 @@
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCSctpTransport-maxChannels.html [ Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCPeerConnection-ondatachannel.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCPeerConnection-ondatachannel.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simplecall.https.html [ Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/protocol/rtp-clockrate.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/protocol/rtp-clockrate.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/protocol/rtp-clockrate.html [ Skip Timeout ]
@@ -3517,8 +3503,6 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc-extensions/RTCRtpSynchronizationSource-captureTimestamp.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc-extensions/RTCRtpSynchronizationSource-senderCaptureTimeOffset.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc-extensions/RTCRtpSynchronizationSource-senderCaptureTimeOffset.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCDataChannel-send.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCDataChannel-send.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCDataChannel-iceRestart.html [ Skip Timeout ]
@@ -3534,39 +3518,18 @@
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/protocol/ice-state.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/protocol/ice-state.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-videoDetectorTest.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-videoDetectorTest.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCDataChannel-bufferedAmount.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCDataChannel-bufferedAmount.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCDataChannel-bufferedAmount.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState-disconnected.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-iceRestart.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-iceRestart.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCDtlsTransport-state.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCDtlsTransport-state.html [ Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/ice-state.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/ice-state.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac10.14 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-clockrate.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-clockrate.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-clockrate.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/simplecall.https.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCDataChannel-close.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/protocol/candidate-exchange.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/protocol/bundle.https.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCPeerConnection-getStats.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-bufferedAmount.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ondatachannel.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ondatachannel.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-close.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-close.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCPeerConnection-addIceCandidate-connectionSetup.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/webrtc/RTCPeerConnection-addIceCandidate-connectionSetup.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/video-rvfc/request-video-frame-callback-webrtc.https.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/protocol/rtp-demuxing.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCIceConnectionState-candidate-pair.https.html [ Skip Timeout ]
@@ -3684,7 +3647,6 @@
 crbug.com/626703 [ Linux ] external/wpt/url/a-element.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/websockets/Create-url-with-space.any.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/webrtc/RTCDTMFSender-ontonechange-long.https.html [ Failure Pass ]
-crbug.com/626703 [ Mac ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange-long.https.html [ Failure Pass ]
 
 ### virtual/prerender/external/wpt/speculation-rules/prerender
 crbug.com/1126305 [ Mac10.15 ] virtual/prerender/external/wpt/speculation-rules/prerender/local-storage.html [ Skip Timeout ]
@@ -3695,9 +3657,6 @@
 crbug.com/367760 external/wpt/svg/pservers/reftests/meshgradient-basic-004.svg [ Failure ]
 crbug.com/367760 external/wpt/svg/pservers/reftests/meshgradient-basic-001.svg [ Failure ]
 
-# Tests pass under virtual/webrtc-wpt-plan-b
-virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-operations.https.html [ Pass ]
-
 crbug.com/1131471 external/wpt/web-locks/clientids.tentative.https.html [ Failure ]
 
 # See also crbug.com/920100 (sheriff 2019-01-09).
@@ -4555,9 +4514,6 @@
 # Sheriff 2019-01-07
 crbug.com/919587 [ Linux ] virtual/threaded/fast/idle-callback/idle_periods.html [ Failure Pass ]
 
-# WebRTC Plan B
-crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-fire.html [ Timeout ]
-
 # Mac doesn't support lowLatency/desynchronized Canvas Contexts.
 crbug.com/922218 [ Mac ] fast/canvas-api/canvas-lowlatency-getContext.html [ Failure ]
 
@@ -4585,13 +4541,6 @@
 # Flaky devtools test for recalculating styles.
 crbug.com/1018177 http/tests/devtools/tracing/timeline-style/timeline-recalculate-styles.js [ Failure Pass ]
 
-# Race: The RTCIceConnectionState can become "connected" before getStats()
-# returns candidate-pair whose state is "succeeded", this sounds like a
-# contradiction.
-
-# This test is not intended to pass on Plan B.
-crbug.com/740501 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html [ Failure Pass Timeout ]
-
 crbug.com/910979 http/tests/html/validation-bubble-oopif-clip.html [ Failure Pass ]
 
 # Sheriff 2019-02-01, 2019-02-19
@@ -5328,9 +5277,6 @@
 # Sheriff 2020-10-09
 crbug.com/1136687 external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html [ Failure Pass ]
 
-# WebRTC: Payload demuxing times out in Plan B. This is expected.
-crbug.com/1139052 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-demuxing.html [ Skip Timeout ]
-
 crbug.com/1137228 [ Mac ] external/wpt/infrastructure/testdriver/click_iframe_crossorigin.sub.html [ Failure Pass ]
 
 # Rename document.featurePolicy to document.permissionsPolicy
@@ -6898,9 +6844,6 @@
 crbug.com/1345235 external/wpt/content-security-policy/style-src/stylehash-basic-blocked.sub.html [ Skip ]
 crbug.com/1334674 [ Mac11 ] http/tests/devtools/oopif/oopif-presentation-console-messages.js [ Failure Pass ]
 
-# Sheriff 2022-07-19
-crbug.com/1345745 virtual/async-script-scheduling-first-paint-or-finished-parsing/wpt_internal/async-script-scheduling/execution-order.html [ Skip ]
-
 # Sheriff 2022-07-21
 crbug.com/1346158 crbug.com/1346158 external/wpt/dom/events/non-cancelable-when-passive/non-passive-mousewheel-event-listener-on-window.html [ Failure Pass ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 01adf69..5f2567a 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -442,12 +442,6 @@
     "args": ["--isolated-app-origins=https://web-platform.test"]
   },
   {
-    "prefix": "webrtc-wpt-plan-b",
-    "platforms": ["Linux", "Mac", "Win"],
-    "bases": ["external/wpt/webrtc"],
-    "args": ["--disable-features=RTCUnifiedPlanByDefault"]
-  },
-  {
     "prefix": "display-compositor-pixel-dump",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [],
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index c9a5243..fd70178b 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -2421,7 +2421,6 @@
 crbug.com/1050754 external/wpt/html/browsers/the-window-object/window-indexed-properties-strict.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/browsers/the-window-object/window-indexed-properties.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/browsers/the-window-object/window-open-noopener.html?indexed [ Timeout ]
-crbug.com/1050754 external/wpt/html/browsers/the-window-object/window-properties.https.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/browsers/the-window-object/window-prototype-chain.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin-domain.sub.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin.sub.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 6975352..869642f 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 4530a81a3700212455c412d6f82b7da4c57a8b1c
+Version: aeab2a940805214a6c715007794ce19f46e131ab
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 a769e8d9..9b71a1c 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
@@ -824,6 +824,13 @@
       ]
      ],
      "table": {
+      "break-before-repeated-footer-instead-of-border-crash.html": [
+       "b277001f4d8f6ca574b6bb16f0d630cd059ebcbc",
+       [
+        null,
+        {}
+       ]
+      ],
       "caption-bottom-margin-at-boundary-crash.html": [
        "edca6635675eef7f2d29e5b535832cb35e455b41",
        [
@@ -874,6 +881,13 @@
         null,
         {}
        ]
+      ],
+      "zero-height-repeated-footer-crash.html": [
+       "4feca8344b75f5702e398a2d2b00ce9d94a8bd9b",
+       [
+        null,
+        {}
+       ]
       ]
      },
      "uncontained-oof-in-inline-after-break-000-crash.html": [
@@ -233865,7 +233879,7 @@
          ]
         ],
         "layers-alpha.html": [
-         "7be5bdb0bb89faf078520d10b3362a277ce9e801",
+         "5e78de6925cc221f9260e7bd395ef0c2cbc7bbdc",
          [
           null,
           [
@@ -233874,7 +233888,23 @@
             "=="
            ]
           ],
-          {}
+          {
+           "fuzzy": [
+            [
+             null,
+             [
+              [
+               0,
+               1
+              ],
+              [
+               0,
+               7850
+              ]
+             ]
+            ]
+           ]
+          }
          ]
         ],
         "layers-endlayer-noop.html": [
@@ -334994,7 +335024,7 @@
       []
      ],
      "webperftestharness.js": [
-      "2fbd0210de906dc530598b23fd0cb9594cb38372",
+      "9627e18a03e49b9152c540eabf5eaa08962c49b8",
       []
      ],
      "webperftestharnessextension.js": [
@@ -337385,7 +337415,7 @@
      []
     ],
     "OWNERS": [
-     "e6ed2ffc906bb65b2656aaa4c26667d5454a98ca",
+     "f72f9b25ab611cf3cd7036cb1348883bd5a0550d",
      []
     ],
     "README.md": [
@@ -337606,6 +337636,10 @@
      "10bcf856eb9258e0845f2fdcb6e08c43ebcdf78a",
      []
     ],
+    "OWNERS": [
+     "7b578a1dc29cfa214dfdb22c21094af0ded1a333",
+     []
+    ],
     "README.md": [
      "2b693728948d898c9c3f81b7e1f2bc7959fa868f",
      []
@@ -377680,6 +377714,13 @@
         {}
        ]
       ],
+      "container-size-shadow-invalidation.html": [
+       "0ee67701a2327614c25c5bb1fd849f72fa27f85d",
+       [
+        null,
+        {}
+       ]
+      ],
       "container-type-computed.html": [
        "0b5e033a0f302e904414447e2a7eb9be8eadfb39",
        [
@@ -399417,7 +399458,7 @@
      ],
      "animations": {
       "calc-interpolation.html": [
-       "f671dbe90711fcc1a96c5ca7a7eb0c10042fe4a7",
+       "40379a13fb2736ed5839e55a58c7cba581155e77",
        [
         null,
         {}
@@ -399474,7 +399515,7 @@
       ]
      ],
      "calc-infinity-nan-computed.html": [
-      "81998c46ae0e73cf7df389a4b4386a97225f5a6f",
+      "f4d66a40f030337e2320a74ca5479a8301e4ae6a",
       [
        null,
        {}
@@ -441462,7 +441503,7 @@
       ]
      ],
      "local-storage.tentative.https.window.js": [
-      "4ccb4c2eb12bb9e485d5a5b6e92bb130ec208756",
+      "2734f12d9e683943aa782aeedff8bbab1b59da0b",
       [
        "html/anonymous-iframe/local-storage.tentative.https.window.html",
        {
@@ -441520,7 +441561,7 @@
       ]
      ],
      "session-storage.tentative.https.window.js": [
-      "5af2dc2b3bdf1957ef5cfafb25c8c54d66c425a2",
+      "94fbd842ac6729684931b92c43bf9efacff94828",
       [
        "html/anonymous-iframe/session-storage.tentative.https.window.html",
        {
@@ -446113,7 +446154,7 @@
        ]
       ],
       "window-properties.https.html": [
-       "980db277597d51bd13b7c9f23d511c9c620c6c97",
+       "c61fb04ae24ab8ec4c9c385dc99e25f35891b756",
        [
         null,
         {}
@@ -472852,14 +472893,14 @@
       },
       "the-frameset-and-frame-elements": {
        "large-cols-relsize.html": [
-        "16cf9a416ba405de3f3d31ad91f60d0fea30a850",
+        "f056aa7e30b8754d19f5b597fd954aaba588b049",
         [
          null,
          {}
         ]
        ],
        "large-rows-relsize.html": [
-        "24f34f51c12bf7a73f7f75a4e011c53cf05d3bd2",
+        "c33cd44adf62089d6c289c1a4e9d5043c778e5ec",
         [
          null,
          {}
@@ -479312,7 +479353,7 @@
         ]
        ],
        "input-untrusted-key-event.html": [
-        "607b0c51ef6ec49242fc0da77750e8886bc5068e",
+        "eb96b6fd956464351be17851a0b7c770c58ea934",
         [
          null,
          {}
@@ -497168,6 +497209,15 @@
      }
     }
    },
+   "mediacapture-extensions": {
+    "GUM-backgroundBlur.https.html": [
+     "605a4e0831914834ac4a5587bbdb7e87cde74e59",
+     [
+      null,
+      {}
+     ]
+    ]
+   },
    "mediacapture-fromelement": {
     "HTMLCanvasElement-getImageData-noframe.html": [
      "48d445f897d7f80b013204f78f52670e31da1c1c",
@@ -526104,7 +526154,7 @@
       ]
      ],
      "progress-based-animation-timeline.html": [
-      "bd7d797fffa8689ce7518b3328e6b077485bda1e",
+      "96339db2ed5c3b1ec886fce5bf4ab0d37a41bf86",
       [
        null,
        {}
@@ -526132,21 +526182,21 @@
       ]
      ],
      "scroll-timeline-name-computed.tentative.html": [
-      "6b1fccd90ce45335123e4d3707f8e14ea6b129f3",
+      "1acb964ef7455ab30c4c627c24f12b9c186753d0",
       [
        null,
        {}
       ]
      ],
      "scroll-timeline-name-parsing.tentative.html": [
-      "19e674db22cb62c683f1cee8c1738e5136a35ada",
+      "866363de3363733dc687de7ac306b13f91e15f07",
       [
        null,
        {}
       ]
      ],
      "scroll-timeline-shorthand.tentative.html": [
-      "b332652c75afc94b9f1740ce366b1d3ded427ba7",
+      "b340ff34ffe4e2ba0d93014ce114656ec3fb5373",
       [
        null,
        {}
@@ -557438,7 +557488,7 @@
      ]
     },
     "event.data.sub.htm": [
-     "97584c4a390222fe6e1bf3656decc36914ed635d",
+     "6858ef4c690b901ef421e84938eb78250d982ddc",
      [
       null,
       {}
@@ -557758,7 +557808,7 @@
      ]
     ],
     "postMessage_asterisk_xorigin.sub.htm": [
-     "532f28251cb36159946ef3d0e99ce9b4ccbe92e2",
+     "2fab9431ef408cc34fb3929f91c5e5aaedd3caff",
      [
       null,
       {}
@@ -559942,6 +559992,13 @@
       }
      ]
     ],
+    "outbound-rtp.https.html": [
+     "b7f4b06411671fa11e19d7dd297a2ae2a1c30ba1",
+     [
+      null,
+      {}
+     ]
+    ],
     "rtp-stats-creation.html": [
      "3141bc08bf1c2951a9438f1c461d750b69694f9e",
      [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/animations/calc-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-values/animations/calc-interpolation.html
index f671dbe9..40379a1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/animations/calc-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/animations/calc-interpolation.html
@@ -36,9 +36,16 @@
     <div class="target"></div>
   </div>
 </template>
+<div class="target" id="inf-target"></div>
 <script>
+const APPROX_INFINITY = (function() {
+  const target = document.getElementById("inf-target");
+  target.style.letterSpacing = "calc(1px * infinity)";
+  const inf = parseFloat(getComputedStyle(target).letterSpacing);
+  target.remove();
+  return inf;
+}());
 
-const APPROX_INFINITY = 3.35544e+07;
 test_interpolation({
   property: 'left',
   from: '0px',
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-infinity-nan-computed.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-infinity-nan-computed.html
index 81998c4..f4d66a4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-infinity-nan-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-infinity-nan-computed.html
@@ -12,46 +12,46 @@
 <body>
 <div id="target"></div>
 <script>
-const APPROX_INFINITY = 3.35544e+07;
-const APPROX_NEGATIVE_INFINITY = -APPROX_INFINITY;
+const REALLY_LARGE = 1e6;
+const REALLY_LARGE_NEGATIVE = -REALLY_LARGE;
 
 // For <length>
-testComputedValueGreaterOrLowerThan("width", "calc(NaN * 1px)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1px)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1cm)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(NaN * 1rem)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(10.135262721212548pc - 199pt / NaN)", APPROX_INFINITY);
+test_computed_value("width", "calc(NaN * 1px)", "0px");
+testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1px)", REALLY_LARGE);
+testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1cm)", REALLY_LARGE);
+test_computed_value("width", "calc(NaN * 1rem)", "0px");
+test_computed_value("width", "calc(10.135262721212548pc - 199pt / NaN)", "0px");
 
-testComputedValueGreaterOrLowerThan("width", "max(15px, NaN * 1px)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "max(NaN * 1px, 15px)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "min(15px, NaN * 1px)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "min(NaN * 1px, 15px)", APPROX_INFINITY);
+test_computed_value("width", "max(15px, NaN * 1px)", "0px");
+test_computed_value("width", "max(NaN * 1px, 15px)", "0px");
+test_computed_value("width", "min(15px, NaN * 1px)", "0px");
+test_computed_value("width", "min(NaN * 1px, 15px)", "0px");
 
-testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1px - infinity * 1%)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1px + infinity * 1%)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(min(NaN * 1px, infinity * 1px) + max(infinity * 1px, -infinity * 1px))", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("width", "calc(infinity * 1px - max(infinity * 1%, 0%))", APPROX_INFINITY);
+test_computed_value("width", "calc(infinity * 1px - infinity * 1%)", "0px");
+test_computed_value("width", "calc(infinity * 1px + infinity * 1%)", "0px");
+test_computed_value("width", "calc(min(NaN * 1px, infinity * 1px) + max(infinity * 1px, -infinity * 1px))", "0px");
+test_computed_value("width", "calc(infinity * 1px - max(infinity * 1%, 0%))", "0px");
 
-testComputedValueGreaterOrLowerThan("width", "calc(max(infinity * 1px, 10px))", APPROX_INFINITY);
+testComputedValueGreaterOrLowerThan("width", "calc(max(infinity * 1px, 10px))", REALLY_LARGE);
 
-testComputedValueGreaterOrLowerThan("margin-left", "calc(-infinity * 1px)", APPROX_NEGATIVE_INFINITY);
-testComputedValueGreaterOrLowerThan("margin-left", "calc(min(1px, -infinity * 1%))", APPROX_NEGATIVE_INFINITY);
+testComputedValueGreaterOrLowerThan("margin-left", "calc(-infinity * 1px)", REALLY_LARGE_NEGATIVE);
+testComputedValueGreaterOrLowerThan("margin-left", "calc(min(1px, -infinity * 1%))", REALLY_LARGE_NEGATIVE);
 
-testComputedValueGreaterOrLowerThan("margin-left", "calc(-infinity * 1%)", APPROX_NEGATIVE_INFINITY);
-testComputedValueGreaterOrLowerThan("margin-left", "calc(max(10000px, 0px) + min(-infinity * 1px, infinity * 1px))", APPROX_NEGATIVE_INFINITY);
+testComputedValueGreaterOrLowerThan("margin-left", "calc(-infinity * 1%)", REALLY_LARGE_NEGATIVE);
+testComputedValueGreaterOrLowerThan("margin-left", "calc(max(10000px, 0px) + min(-infinity * 1px, infinity * 1px))", REALLY_LARGE_NEGATIVE);
 
-testComputedValueGreaterOrLowerThan("margin-left", "calc(-infinity * 1px - infinity * 1px)", APPROX_NEGATIVE_INFINITY);
-testComputedValueGreaterOrLowerThan("margin-left", "calc(min(-infinity * 1px, 10px))", APPROX_NEGATIVE_INFINITY);
+testComputedValueGreaterOrLowerThan("margin-left", "calc(-infinity * 1px - infinity * 1px)", REALLY_LARGE_NEGATIVE);
+testComputedValueGreaterOrLowerThan("margin-left", "calc(min(-infinity * 1px, 10px))", REALLY_LARGE_NEGATIVE);
 
 // For <time>
-testComputedValueGreaterOrLowerThan("animation-duration", "calc(NaN * 1s)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("animation-duration", "calc(infinity * 1s)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("animation-duration", "calc(1 / 0 * 1s)", APPROX_INFINITY);
-testComputedValueGreaterOrLowerThan("animation-duration", "calc(max(infinity * 1s, 10s)", APPROX_INFINITY);
+test_computed_value("animation-duration", "calc(NaN * 1s)", "0s");
+testComputedValueGreaterOrLowerThan("animation-duration", "calc(infinity * 1s)", REALLY_LARGE);
+test_computed_value("animation-duration", "calc(1 / 0 * 1s)", "0s");
+testComputedValueGreaterOrLowerThan("animation-duration", "calc(max(infinity * 1s, 10s)", REALLY_LARGE);
 
-testComputedValueGreaterOrLowerThan("transition-delay", "calc(-infinity* 1s)", APPROX_NEGATIVE_INFINITY);
-testComputedValueGreaterOrLowerThan("transition-delay", "calc(max(10000s, 0s) + min(-infinity * 1s, infinity * 1s))", APPROX_NEGATIVE_INFINITY);
-testComputedValueGreaterOrLowerThan("transition-delay", "calc(min(-infinity * 1s, 10s))", APPROX_NEGATIVE_INFINITY);
+testComputedValueGreaterOrLowerThan("transition-delay", "calc(-infinity* 1s)", REALLY_LARGE_NEGATIVE);
+testComputedValueGreaterOrLowerThan("transition-delay", "calc(max(10000s, 0s) + min(-infinity * 1s, infinity * 1s))", REALLY_LARGE_NEGATIVE);
+testComputedValueGreaterOrLowerThan("transition-delay", "calc(min(-infinity * 1s, 10s))", REALLY_LARGE_NEGATIVE);
 
 // For <angle>
 testTransformValuesCloseTo("rotate(calc(infinity * 1deg))", 0.0001, "rotate(0deg)");
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/window-properties.https.html b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/window-properties.https.html
index 980db27..c61fb04a 100644
--- a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/window-properties.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/window-properties.https.html
@@ -15,6 +15,8 @@
 <link rel="help" href="https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#dom-window-getselection">
 <link rel="help" href="http://dev.w3.org/csswg/cssom/#widl-def-Window">
 <link rel="help" href="http://dev.w3.org/csswg/cssom-view/#widl-def-Window">
+<iframe id="iframe" style="visibility: hidden" src="/resources/blank.html"></iframe>
+<iframe id="detachedIframe" style="visibility: hidden" src="/resources/blank.html"></iframe>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <div id=log></div>
@@ -323,4 +325,34 @@
   assert_data_propdesc(Object.getOwnPropertyDescriptor(Window.prototype, "constructor"),
                        true, false, true);
 }, "constructor");
+var selfReferentialAccessors = ['window', 'self', 'frames'];
+// Test a variety of access methods
+var detached = window.detachedIframe;
+var detachedWindow = detached.contentWindow;
+detachedIframe.remove();
+selfReferentialAccessors.forEach(function(id) {
+  test(function() {
+    assert_equals(eval(`window.${id}`), window);
+    assert_equals(window[id], window);
+    assert_equals(Object.getOwnPropertyDescriptor(window, id).get.call(window), window);
+    assert_equals(Reflect.get(window, id), window);
+  }, "Window readonly getter: " + id);
+
+  test(function() {
+    var globalObject = iframe.contentWindow;
+    var _eval = globalObject.eval;
+    assert_equals(globalObject.eval(`window.${id}`), globalObject);
+    assert_equals(globalObject[id], globalObject);
+    assert_equals(_eval(`Object.getOwnPropertyDescriptor(window, "${id}").get.call(window)`), globalObject);
+    assert_equals(_eval(`Reflect.get(window, "${id}")`), globalObject);
+  }, "Window readonly getter in iframe: " + id);
+  test(function() {
+    var globalObject = detachedWindow;
+    var _eval = globalObject.eval;
+    assert_equals(_eval(`window.${id}`), globalObject);
+    assert_equals(globalObject[id], globalObject);
+    assert_equals(_eval(`Object.getOwnPropertyDescriptor(window, "${id}").get.call(window)`), globalObject);
+    assert_equals(_eval(`Reflect.get(window, "${id}")`), globalObject);
+  }, "Window readonly getter in detached iframe: " + id);
+});
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/input-untrusted-key-event.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/input-untrusted-key-event.html
index 607b0c5..eb96b6f 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/input-untrusted-key-event.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/input-untrusted-key-event.html
@@ -8,7 +8,10 @@
 <body>
 <div id="log"></div>
 <form id="input_form">
-  <input type="submit" value="submit"><br>
+  <fieldset>
+    <input type="radio" name="radio" value="1">
+    <input type="radio" name="radio" value="2">
+  </fieldset>
 </form>
 <script type="module">
 import inputTypes from "./input-types.js";
@@ -19,24 +22,47 @@
   assert_true(false, 'form should not be submitted');
 });
 
+const radioButton = document.querySelector("input[type=radio]");
+radioButton.addEventListener("click", function(e) {
+  assert_true(false, `input radio should not be clicked`);
+});
+radioButton.addEventListener("focus", function(e) {
+  assert_true(false, `input radio should not be focused on`);
+});
+radioButton.addEventListener("change", function(e) {
+  assert_true(false, `input radio should not be changed`);
+});
+radioButton.addEventListener("input", function(e) {
+  assert_true(false, `input radio should not have been inputted`);
+});
+
 // Create and append input elements
 for (const inputType of inputTypes) {
+  if (inputType == "radio") {
+    continue;
+  }
+
   let input = document.createElement("input");
   input.type = inputType;
   form.appendChild(input);
-}
 
-const submit = document.querySelector("input[type=submit]");
-submit.addEventListener("click", function(e) {
-  assert_true(false, 'input submit should not be clicked');
-});
+  input.addEventListener("click", function(e) {
+    assert_true(false, `input ${inputType} should not be clicked`);
+  });
+  input.addEventListener("focus", function(e) {
+    assert_true(false, `input ${inputType} should not be focused on`);
+  });
+  input.addEventListener("change", function(e) {
+    assert_true(false, `input ${inputType} should not be changed`);
+  });
+  input.addEventListener("input", function(e) {
+    assert_true(false, `input ${inputType} should not have been inputted`);
+  });
+}
 
 // Start tests
 for (const inputType of inputTypes) {
   let input = document.querySelector(`input[type=${inputType}]`);
-  input.addEventListener("click", function(e) {
-    assert_true(false, `input ${inputType} should not be clicked`);
-  });
 
   test(() => {
     // keyCode: Enter
@@ -66,7 +92,35 @@
         key: " ",
       })
     );
-  }, `Dipatching untrusted keypress events to input ${inputType} should not cause form submission or click event`);
+
+    // keyCode: Tab
+    input.dispatchEvent(
+      new KeyboardEvent("keypress", {
+        keyCode: 9,
+      })
+    );
+
+    // key: Tab
+    input.dispatchEvent(
+      new KeyboardEvent("keypress", {
+        key: "Tab",
+      })
+    );
+
+    // keyCode: ArrowUp
+    input.dispatchEvent(
+      new KeyboardEvent("keypress", {
+        keyCode: 38,
+      })
+    );
+
+    // key: ArrowUp
+    input.dispatchEvent(
+      new KeyboardEvent("keypress", {
+        key: "ArrowUp",
+      })
+    );
+  }, `Dispatching untrusted keypress events to input ${inputType} should not cause submission, click, change, input, or focus events`);
 
   test(() => {
     // keyCode: Enter
@@ -116,7 +170,55 @@
         key: " ",
       })
     );
-  }, `Dipatching untrusted keyup/keydown event to input ${inputType} should not cause form submission or click event`);
+
+    // keyCode: Tab
+    input.dispatchEvent(
+      new KeyboardEvent("keydown", {
+        keyCode: 9,
+      })
+    );
+    input.dispatchEvent(
+      new KeyboardEvent("keyup", {
+        keyCode: 9,
+      })
+    );
+
+    // key: Tab
+    input.dispatchEvent(
+      new KeyboardEvent("keydown", {
+        key: "Tab",
+      })
+    );
+    input.dispatchEvent(
+      new KeyboardEvent("keyup", {
+        key: "Tab",
+      })
+    );
+
+    // keyCode: ArrowUp
+    input.dispatchEvent(
+      new KeyboardEvent("keydown", {
+        keyCode: 38,
+      })
+    );
+    input.dispatchEvent(
+      new KeyboardEvent("keyup", {
+        keyCode: 38,
+      })
+    );
+
+    // key: ArrowUp
+    input.dispatchEvent(
+      new KeyboardEvent("keydown", {
+        key: "ArrowUp",
+      })
+    );
+    input.dispatchEvent(
+      new KeyboardEvent("keyup", {
+        key: "ArrowUp",
+      })
+    );
+  }, `Dispatching untrusted keyup/keydown events to input ${inputType} should not cause submission, click, change, input, or focus events`);
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/resources/webperftestharness.js b/third_party/blink/web_tests/external/wpt/user-timing/resources/webperftestharness.js
index 2fbd021..9627e18 100644
--- a/third_party/blink/web_tests/external/wpt/user-timing/resources/webperftestharness.js
+++ b/third_party/blink/web_tests/external/wpt/user-timing/resources/webperftestharness.js
@@ -110,12 +110,12 @@
 
 function test_greater_than(value, greater_than, msg, properties)
 {
-    wp_test(function () { assert_true(value > greater_than, msg); }, msg, properties);
+    wp_test(function () { assert_greater_than(value, greater_than, msg); }, msg, properties);
 }
 
 function test_greater_or_equals(value, greater_than, msg, properties)
 {
-    wp_test(function () { assert_true(value >= greater_than, msg); }, msg, properties);
+    wp_test(function () { assert_greater_than_equal(value, greater_than, msg); }, msg, properties);
 }
 
 function test_not_equals(value, notequals, msg, properties)
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-values/animations/calc-interpolation-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-values/animations/calc-interpolation-expected.txt
new file mode 100644
index 0000000..36b716c4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-values/animations/calc-interpolation-expected.txt
@@ -0,0 +1,144 @@
+This is a testharness.js-based test.
+Found 140 tests; 132 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+FAIL CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-8.50705e+37px] assert_equals: expected "- 3.36e + 07px " but got "- 8.39e + 06px "
+PASS CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (0) should be [0px]
+FAIL CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (0.25) should be [8.50705e+37px] assert_equals: expected "3.36e + 07px " but got "8.39e + 06px "
+FAIL CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (0.5) should be [1.70141e+38px] assert_equals: expected "3.36e + 07px " but got "1.68e + 07px "
+FAIL CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (0.75) should be [2.552115e+38px] assert_equals: expected "3.36e + 07px " but got "2.52e + 07px "
+PASS CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px]
+PASS CSS Transitions: property <left> from [0px] to [calc(infinity * 1px)] at (1.25) should be [4.2535250000000006e+38px]
+FAIL CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-8.50705e+37px] assert_equals: expected "- 3.36e + 07px " but got "- 8.39e + 06px "
+PASS CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (0) should be [0px]
+FAIL CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (0.25) should be [8.50705e+37px] assert_equals: expected "3.36e + 07px " but got "8.39e + 06px "
+FAIL CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (0.5) should be [1.70141e+38px] assert_equals: expected "3.36e + 07px " but got "1.68e + 07px "
+FAIL CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (0.75) should be [2.552115e+38px] assert_equals: expected "3.36e + 07px " but got "2.52e + 07px "
+PASS CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px]
+PASS CSS Transitions with transition: all: property <left> from [0px] to [calc(infinity * 1px)] at (1.25) should be [4.2535250000000006e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-3.40282e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0) should be [3.40282e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0.25) should be [3.40282e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0.5) should be [3.40282e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0.75) should be [3.40282e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px]
+PASS CSS Animations: property <left> from [0px] to [calc(infinity * 1px)] at (1.25) should be [3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0) should be [3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0.25) should be [3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0.5) should be [3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (0.75) should be [3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px]
+PASS Web Animations: property <left> from [0px] to [calc(infinity * 1px)] at (1.25) should be [3.40282e+38px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [-10px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [0px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [10px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [20px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [30px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [40px]
+PASS CSS Transitions: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [50px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [-10px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [0px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [10px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [20px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [30px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [40px]
+PASS CSS Transitions with transition: all: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [50px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [-10px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [0px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [10px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [20px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [30px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [40px]
+PASS CSS Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [50px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [-10px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [0px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [10px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [20px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [30px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [40px]
+PASS Web Animations: property <left> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [50px]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [calc(((50% - 25px) * 1.25) + ((100% - 10px) * -0.25))]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [calc(50% - 25px)]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [calc(((50% - 25px) * 0.75) + ((100% - 10px) * 0.25))]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [calc(((50% - 25px) * 0.5) + ((100% - 10px) * 0.5))]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [calc(((50% - 25px) * 0.25) + ((100% - 10px) * 0.75))]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [calc(100% - 10px)]
+PASS CSS Transitions: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [calc(((50% - 25px) * -0.25) + ((100% - 10px) * 1.25))]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [calc(((50% - 25px) * 1.25) + ((100% - 10px) * -0.25))]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [calc(50% - 25px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [calc(((50% - 25px) * 0.75) + ((100% - 10px) * 0.25))]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [calc(((50% - 25px) * 0.5) + ((100% - 10px) * 0.5))]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [calc(((50% - 25px) * 0.25) + ((100% - 10px) * 0.75))]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [calc(100% - 10px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [calc(((50% - 25px) * -0.25) + ((100% - 10px) * 1.25))]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [calc(((50% - 25px) * 1.25) + ((100% - 10px) * -0.25))]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [calc(50% - 25px)]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [calc(((50% - 25px) * 0.75) + ((100% - 10px) * 0.25))]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [calc(((50% - 25px) * 0.5) + ((100% - 10px) * 0.5))]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [calc(((50% - 25px) * 0.25) + ((100% - 10px) * 0.75))]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [calc(100% - 10px)]
+PASS CSS Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [calc(((50% - 25px) * -0.25) + ((100% - 10px) * 1.25))]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [calc(((50% - 25px) * 1.25) + ((100% - 10px) * -0.25))]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [calc(50% - 25px)]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [calc(((50% - 25px) * 0.75) + ((100% - 10px) * 0.25))]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.5) should be [calc(((50% - 25px) * 0.5) + ((100% - 10px) * 0.5))]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.75) should be [calc(((50% - 25px) * 0.25) + ((100% - 10px) * 0.75))]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1) should be [calc(100% - 10px)]
+PASS Web Animations: property <text-indent> from [calc(50% - 25px)] to [calc(100% - 10px)] at (1.25) should be [calc(((50% - 25px) * -0.25) + ((100% - 10px) * 1.25))]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (-0.25) should be [-25px]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (0) should be [0em]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (0.25) should be [25px]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (0.5) should be [50px]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (0.75) should be [75px]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (1) should be [100px]
+PASS CSS Transitions: property <text-indent> from [0em] to [100px] at (1.25) should be [125px]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (-0.25) should be [-25px]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (0) should be [0em]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (0.25) should be [25px]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (0.5) should be [50px]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (0.75) should be [75px]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (1) should be [100px]
+PASS CSS Transitions with transition: all: property <text-indent> from [0em] to [100px] at (1.25) should be [125px]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (-0.25) should be [-25px]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (0) should be [0em]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (0.25) should be [25px]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (0.5) should be [50px]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (0.75) should be [75px]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (1) should be [100px]
+PASS CSS Animations: property <text-indent> from [0em] to [100px] at (1.25) should be [125px]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (-0.25) should be [-25px]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (0) should be [0em]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (0.25) should be [25px]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (0.5) should be [50px]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (0.75) should be [75px]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (1) should be [100px]
+PASS Web Animations: property <text-indent> from [0em] to [100px] at (1.25) should be [125px]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (-0.25) should be [calc(0% + -25px)]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (0) should be [0%]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (0.25) should be [calc(0% + 25px)]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (0.5) should be [calc(0% + 50px)]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (0.75) should be [calc(0% + 75px)]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (1) should be [calc(0% + 100px)]
+PASS CSS Transitions: property <text-indent> from [0%] to [100px] at (1.25) should be [calc(0% + 125px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (-0.25) should be [calc(0% + -25px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (0) should be [0%]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (0.25) should be [calc(0% + 25px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (0.5) should be [calc(0% + 50px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (0.75) should be [calc(0% + 75px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (1) should be [calc(0% + 100px)]
+PASS CSS Transitions with transition: all: property <text-indent> from [0%] to [100px] at (1.25) should be [calc(0% + 125px)]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (-0.25) should be [calc(0% + -25px)]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (0) should be [0%]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (0.25) should be [calc(0% + 25px)]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (0.5) should be [calc(0% + 50px)]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (0.75) should be [calc(0% + 75px)]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (1) should be [calc(0% + 100px)]
+PASS CSS Animations: property <text-indent> from [0%] to [100px] at (1.25) should be [calc(0% + 125px)]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (-0.25) should be [calc(0% + -25px)]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (0) should be [0%]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (0.25) should be [calc(0% + 25px)]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (0.5) should be [calc(0% + 50px)]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (0.75) should be [calc(0% + 75px)]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (1) should be [calc(0% + 100px)]
+PASS Web Animations: property <text-indent> from [0%] to [100px] at (1.25) should be [calc(0% + 125px)]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-values/calc-infinity-nan-computed-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-values/calc-infinity-nan-computed-expected.txt
new file mode 100644
index 0000000..1bde8d7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-values/calc-infinity-nan-computed-expected.txt
@@ -0,0 +1,42 @@
+This is a testharness.js-based test.
+FAIL Property width value 'calc(NaN * 1px)' assert_equals: expected "0px" but got "3.35544e+07px"
+PASS Property width value 'calc(infinity * 1px)'
+PASS Property width value 'calc(infinity * 1cm)'
+FAIL Property width value 'calc(NaN * 1rem)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'calc(10.135262721212548pc - 199pt / NaN)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'max(15px, NaN * 1px)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'max(NaN * 1px, 15px)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'min(15px, NaN * 1px)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'min(NaN * 1px, 15px)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'calc(infinity * 1px - infinity * 1%)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'calc(infinity * 1px + infinity * 1%)' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'calc(min(NaN * 1px, infinity * 1px) + max(infinity * 1px, -infinity * 1px))' assert_equals: expected "0px" but got "3.35544e+07px"
+FAIL Property width value 'calc(infinity * 1px - max(infinity * 1%, 0%))' assert_equals: expected "0px" but got "3.35544e+07px"
+PASS Property width value 'calc(max(infinity * 1px, 10px))'
+PASS Property margin-left value 'calc(-infinity * 1px)'
+PASS Property margin-left value 'calc(min(1px, -infinity * 1%))'
+PASS Property margin-left value 'calc(-infinity * 1%)'
+PASS Property margin-left value 'calc(max(10000px, 0px) + min(-infinity * 1px, infinity * 1px))'
+PASS Property margin-left value 'calc(-infinity * 1px - infinity * 1px)'
+PASS Property margin-left value 'calc(min(-infinity * 1px, 10px))'
+FAIL Property animation-duration value 'calc(NaN * 1s)' assert_equals: expected "0s" but got "1.79769e+308s"
+PASS Property animation-duration value 'calc(infinity * 1s)'
+FAIL Property animation-duration value 'calc(1 / 0 * 1s)' assert_equals: expected "0s" but got "1.79769e+308s"
+PASS Property animation-duration value 'calc(max(infinity * 1s, 10s)'
+PASS Property transition-delay value 'calc(-infinity* 1s)'
+PASS Property transition-delay value 'calc(max(10000s, 0s) + min(-infinity * 1s, infinity * 1s))'
+PASS Property transition-delay value 'calc(min(-infinity * 1s, 10s))'
+PASS Property rotate(calc(infinity * 1deg)) value expected same with rotate(0deg) in +/-0.0001
+PASS Property rotate(calc(-infinity * 1deg)) value expected same with rotate(0deg) in +/-0.0001
+PASS Property rotate(calc(NaN * 1deg)) value expected same with rotate(0deg) in +/-0.0001
+PASS Property rotate(calc(infinity * 1turn)) value expected same with rotate(0turn) in +/-0.0001
+PASS Property rotate(calc(-infinity * 1turn)) value expected same with rotate(0turn) in +/-0.0001
+PASS Property rotate(calc(NaN * 1turn)) value expected same with rotate(0turn) in +/-0.0001
+PASS Property rotate(calc(infinity * 1rad)) value expected same with rotate(0rad) in +/-0.0001
+PASS Property rotate(calc(-infinity * 1rad)) value expected same with rotate(0rad) in +/-0.0001
+PASS Property rotate(calc(NaN * 1rad)) value expected same with rotate(0rad) in +/-0.0001
+PASS Property rotate(calc(infinity * 1grad)) value expected same with rotate(0grad) in +/-0.0001
+PASS Property rotate(calc(-infinity * 1grad)) value expected same with rotate(0grad) in +/-0.0001
+PASS Property rotate(calc(NaN * 1grad)) value expected same with rotate(0grad) in +/-0.0001
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCConfiguration-iceTransportPolicy-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCConfiguration-iceTransportPolicy-expected.txt
deleted file mode 100644
index 0a60695..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCConfiguration-iceTransportPolicy-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This is a testharness.js-based test.
-PASS new RTCPeerConnection() should have default iceTransportPolicy all
-PASS new RTCPeerConnection({ iceTransportPolicy: undefined }) should have default iceTransportPolicy all
-PASS new RTCPeerConnection({ iceTransportPolicy: 'all' }) should succeed
-PASS new RTCPeerConnection({ iceTransportPolicy: 'relay' }) should succeed
-PASS setConfiguration({ iceTransportPolicy: 'relay' }) with initial iceTransportPolicy all should succeed
-PASS setConfiguration({ iceTransportPolicy: 'all' }) with initial iceTransportPolicy relay should succeed
-PASS setConfiguration({}) with initial iceTransportPolicy relay should set new value to all
-PASS new RTCPeerConnection(config) - with invalid iceTransportPolicy should throw TypeError
-PASS setConfiguration(config) - with invalid iceTransportPolicy should throw TypeError
-PASS new RTCPeerConnection(config) - with none iceTransportPolicy should throw TypeError
-PASS setConfiguration(config) - with none iceTransportPolicy should throw TypeError
-PASS new RTCPeerConnection(config) - with null iceTransportPolicy should throw TypeError
-PASS setConfiguration(config) - with null iceTransportPolicy should throw TypeError
-FAIL new RTCPeerConnection({ iceTransports: 'relay' }) should have no effect assert_equals: expected "all" but got "relay"
-FAIL new RTCPeerConnection({ iceTransports: 'invalid' }) should have no effect Failed to construct 'RTCPeerConnection': Failed to read the 'iceTransports' property from 'RTCConfiguration': The provided value 'invalid' is not a valid enum value of type RTCIceTransportPolicy.
-FAIL new RTCPeerConnection({ iceTransports: null }) should have no effect Failed to construct 'RTCPeerConnection': Failed to read the 'iceTransports' property from 'RTCConfiguration': The provided value 'null' is not a valid enum value of type RTCIceTransportPolicy.
-FAIL iceTransportPolicy "relay" on offerer should prevent candidate gathering promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL iceTransportPolicy "relay" on answerer should prevent candidate gathering promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Changing iceTransportPolicy from "all" to "relay" causes an ICE restart which should fail, with no new candidates promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Changing iceTransportPolicy from "relay" to "all" causes an ICE restart which should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Changing iceTransportPolicy from "all" to "relay", and back to "all" prompts an ICE restart promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Changing iceTransportPolicy from "all" to "relay" on the answerer has no effect on a subsequent offer/answer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
deleted file mode 100644
index 04e66b8c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS insertDTMF() should succeed if tones contains valid DTMF characters
-PASS insertDTMF() should throw InvalidCharacterError if tones contains invalid DTMF characters
-FAIL insertDTMF() should throw InvalidStateError if transceiver is stopped Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL insertDTMF() should throw InvalidStateError if transceiver.currentDirection is recvonly promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL insertDTMF() should throw InvalidStateError if transceiver.currentDirection is inactive promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS insertDTMF() should set toneBuffer to provided tones normalized, with old tones overridden
-PASS insertDTMF() after remove and close should reject
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
deleted file mode 100644
index 4db5813..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-PASS insertDTMF() with default duration and intertoneGap should fire tonechange events at the expected time
-PASS insertDTMF() with explicit duration and intertoneGap should fire tonechange events at the expected time
-PASS insertDTMF('') should not fire any tonechange event, including for '' tone
-PASS insertDTMF() with duration less than 40 should be clamped to 40
-PASS insertDTMF() with interToneGap less than 30 should be clamped to 30
-PASS insertDTMF with comma should delay next tonechange event for a constant 2000ms
-FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_equals: Expect there to be only one tranceiver in pc expected 1 but got 0
-PASS Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones
-PASS Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones
-PASS Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing
-FAIL Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-PASS Tone change event constructor works
-PASS Tone change event with unexpected name should not crash
-FAIL Tone change event init optional parameters Failed to construct 'RTCDTMFToneChangeEvent': 2 arguments required, but only 1 present.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send-expected.txt
deleted file mode 100644
index c120ead7..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-send-expected.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-This is a testharness.js-based test.
-PASS Calling send() when data channel is in connecting state should throw InvalidStateError
-PASS Datachannel should be able to send simple string and receive as string
-PASS Datachannel should be able to send unicode string and receive as unicode string
-PASS Datachannel should ignore binaryType and always receive string message as string
-PASS Datachannel should be able to send an empty string and receive an empty string
-PASS Datachannel should be able to send Uint8Array message and receive as ArrayBuffer
-PASS Datachannel should be able to send ArrayBuffer message and receive as ArrayBuffer
-PASS Datachannel should be able to send an empty ArrayBuffer message and receive as ArrayBuffer
-FAIL Datachannel should be able to send Blob message and receive as ArrayBuffer promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet"
-FAIL Datachannel should be able to send ArrayBuffer message and receive as Blob promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to set the 'binaryType' property on 'RTCDataChannel': Blob support not implemented yet"
-FAIL Datachannel binaryType should receive message as Blob by default assert_equals: Expect initial binaryType value to be blob expected "blob" but got "arraybuffer"
-FAIL Datachannel sending multiple messages with different types should succeed and be received assert_unreached: Unexpected promise rejection: NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet Reached unreachable code
-FAIL Datachannel send() up to max size should succeed, above max size should fail promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'maxMessageSize')"
-PASS Negotiated datachannel should be able to send simple string and receive as string
-PASS Negotiated datachannel should be able to send unicode string and receive as unicode string
-PASS Negotiated datachannel should ignore binaryType and always receive string message as string
-PASS Negotiated datachannel should be able to send an empty string and receive an empty string
-PASS Negotiated datachannel should be able to send Uint8Array message and receive as ArrayBuffer
-PASS Negotiated datachannel should be able to send ArrayBuffer message and receive as ArrayBuffer
-PASS Negotiated datachannel should be able to send an empty ArrayBuffer message and receive as ArrayBuffer
-FAIL Negotiated datachannel should be able to send Blob message and receive as ArrayBuffer promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet"
-FAIL Negotiated datachannel should be able to send ArrayBuffer message and receive as Blob promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to set the 'binaryType' property on 'RTCDataChannel': Blob support not implemented yet"
-FAIL Negotiated datachannel binaryType should receive message as Blob by default assert_equals: Expect initial binaryType value to be blob expected "blob" but got "arraybuffer"
-FAIL Negotiated datachannel sending multiple messages with different types should succeed and be received assert_unreached: Unexpected promise rejection: NotSupportedError: Failed to execute 'send' on 'RTCDataChannel': Blob support not implemented yet Reached unreachable code
-FAIL Negotiated datachannel send() up to max size should succeed, above max size should fail promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'maxMessageSize')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
deleted file mode 100644
index 4772145..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-getRemoteCertificates-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL RTCDtlsTransport.prototype.getRemoteCertificates Cannot read properties of null (reading 'state')
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
deleted file mode 100644
index 34f82556..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL DTLS transport goes to connected state promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
-FAIL close() causes the local transport to close immediately promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
-FAIL close() causes the other end's DTLS transport to close promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
-FAIL stop bundled transceiver retains dtls transport state promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-expected.txt
deleted file mode 100644
index 465184fe..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceTransport-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Two connected iceTransports should has matching local/remote candidates returned assert_true: Expect pc.sctp to be instantiated from RTCSctpTransport expected true got false
-FAIL Unconnected iceTransport should have empty remote candidates and selected pair assert_true: Expect pc.sctp to be instantiated from RTCSctpTransport expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-SLD-SRD-timing.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-SLD-SRD-timing.https-expected.txt
deleted file mode 100644
index 4904bb8d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-SLD-SRD-timing.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL setLocalDescription and setRemoteDescription are not racy promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-connectionSetup-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-connectionSetup-expected.txt
deleted file mode 100644
index 265f4ad..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-connectionSetup-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL Candidates are added dynamically; connection should work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Candidates are added at PC1; connection should work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Candidates are added at PC2; connection should work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-timing.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-timing.https-expected.txt
deleted file mode 100644
index d488975..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-timing.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL addIceCandidate is not resolved first if 2x setLocalDescription operations are pending promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addIceCandidate and setLocalDescription are resolved in the correct order, as defined by the operations chain specification promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL onicecandidate fires after resolving setLocalDescription in offerer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL onicecandidate fires after resolving setLocalDescription in answerer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
deleted file mode 100644
index 76f83e6..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS addTrack when pc is closed should throw InvalidStateError
-FAIL addTrack with single track argument and no stream should succeed assert_equals: Expect only one transceiver with sender added expected 1 but got 0
-PASS addTrack with single track argument and single stream should succeed
-FAIL addTrack with single track argument and multiple streams should succeed promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'addTrack' on 'RTCPeerConnection': Adding a track to multiple streams is not supported."
-PASS Adding the same track multiple times should throw InvalidAccessError
-FAIL addTrack with existing sender with null track, same kind, and recvonly direction should reuse sender promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTrack with existing sender that has not been used to send should reuse the sender promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTrack with existing sender that has been used to send should create new sender promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTrack with existing sender with null track, different kind, and recvonly direction should create new sender promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Adding more tracks does not generate more candidates if bundled promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling addTrack while sRD(offer) is pending should allow the new remote transceiver to be the same one that addTrack creates assert_equals: Should have 1 transceiver expected 1 but got 0
-FAIL When addTrack is called while sRD is in progress, and both addTrack and sRD add a transceiver of different media types, the addTrack transceiver should come first, and then the sRD transceiver. promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
deleted file mode 100644
index 1cfe5b07..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This is a testharness.js-based test.
-FAIL addTransceiver() with string argument as invalid kind should throw TypeError assert_throws_js: function "() => pc.addTransceiver('invalid')" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." ("InvalidStateError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-FAIL addTransceiver('audio') should return an audio transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL addTransceiver('video') should return a video transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL addTransceiver() with direction sendonly should have result transceiver.direction be the same Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL addTransceiver() with direction inactive should have result transceiver.direction be the same Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-PASS addTransceiver() with invalid direction should throw TypeError
-FAIL addTransceiver(track) should have result with sender.track be given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(track) multiple times should create multiple transceivers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver() with rid containing invalid non-alphanumeric characters should throw TypeError assert_throws_js: function "() =>
-      pc.addTransceiver('audio', {
-        sendEncodings: [{
-          rid: '@Invalid!'
-        }]
-      })" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." ("InvalidStateError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-FAIL addTransceiver() with rid longer than 16 characters should throw TypeError assert_throws_js: function "() =>
-      pc.addTransceiver('audio', {
-        sendEncodings: [{
-          rid: 'a'.repeat(17)
-        }]
-      })" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." ("InvalidStateError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-FAIL addTransceiver() with valid rid value should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL addTransceiver() with valid sendEncodings should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-candidate-in-sdp.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-candidate-in-sdp.https-expected.txt
deleted file mode 100644
index d8de3cd..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-candidate-in-sdp.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL localDescription contains candidates promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-connectionState.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-connectionState.https-expected.txt
deleted file mode 100644
index 7510932b..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-connectionState.https-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS Initial connectionState should be new
-PASS Closing the connection should set connectionState to closed
-PASS connection with one data channel should eventually have connected connection state
-FAIL connection with one data channel should eventually have transports in connected state Cannot read properties of null (reading 'transport')
-PASS connectionState remains new when not adding remote ice candidates
-PASS connectionState transitions to connected via connecting
-PASS Closing a PeerConnection should not fire connectionstatechange event
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
deleted file mode 100644
index 1d7e363..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL createOffer() with no argument from newly created RTCPeerConnection should succeed assert_false: Expect offer to not be instance of RTCSessionDescription expected false got true
-PASS createOffer() and then setLocalDescription() should succeed
-PASS createOffer() after connection is closed should reject with InvalidStateError
-FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL createOffer() should fail when signaling state is not stable or have-local-offer assert_unreached: Should have rejected: undefined Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState-expected.txt
deleted file mode 100644
index d5b892e8..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-explicit-rollback-iceGatheringState-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL rolling back an ICE restart when gathering is complete should not result in iceGatheringState changes promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "complete" promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setLocalDescription(rollback) of original offer should cause iceGatheringState to reach "new" when starting in "gathering" promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
deleted file mode 100644
index f9e5faf2..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS getStats() with no argument should succeed
-PASS getStats(null) should succeed
-PASS getStats() with track not added to connection should reject with InvalidAccessError
-PASS getStats() with track added via addTrack should succeed
-FAIL getStats() with track added via addTransceiver should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL getStats() with track associated with both sender and receiver should reject with InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
-PASS getStats() track with stream returns peer-connection and outbound-rtp stats
-PASS getStats() track without stream returns peer-connection and outbound-rtp stats
-PASS getStats() audio outbound-rtp contains all mandatory stats
-PASS getStats() video outbound-rtp contains all mandatory stats
-FAIL getStats() audio and video validate all mandatory stats assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined"
-PASS getStats() on track associated with RTCRtpSender should return stats report containing outbound-rtp stats
-PASS getStats() on track associated with RTCRtpReceiver should return stats report containing inbound-rtp stats
-PASS getStats() inbound-rtp contains all mandatory stats
-FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_equals: Expect dictionary.sdpFmtpLine to be string expected "string" but got "undefined" Reached unreachable code
-FAIL getStats(track) should not work if multiple senders have the same track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS RTCStats.timestamp increases with time passing
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-helper-test-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-helper-test-expected.txt
deleted file mode 100644
index 1b5c8dc..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-helper-test-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Setting up a connection using helpers and defaults should work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
deleted file mode 100644
index 1493d5a..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS Initial iceConnectionState should be new
-PASS Closing the connection should set iceConnectionState to closed
-PASS connection with one data channel should eventually have connected or completed connection state
-FAIL connection with one data channel should eventually have connected connection state Cannot read properties of null (reading 'transport')
-PASS connection with audio track should eventually have connected connection state
-PASS connection with audio and video tracks should eventually have connected connection state
-FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-PASS Responder ICE connection state behaves as expected
-PASS Closing a PeerConnection should not fire iceconnectionstatechange event
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
deleted file mode 100644
index c15b2f5..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceGatheringState-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-PASS Initial iceGatheringState should be new
-PASS iceGatheringState should eventually become complete after setLocalDescription
-FAIL setLocalDescription(reoffer) with no new transports should not cause iceGatheringState to change promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS setLocalDescription() with no transports should not cause iceGatheringState to change
-FAIL setLocalDescription(reoffer) with a new transport should cause iceGatheringState to go to "checking" and then "complete" promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sRD does not cause ICE gathering state changes promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL renegotiation that closes all transports should result in ICE gathering state "new" promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL connection with one data channel should eventually have connected connection state promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onsignalingstatechanged.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onsignalingstatechanged.https-expected.txt
deleted file mode 100644
index 1edb058..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onsignalingstatechanged.https-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negotiation methods fire signalingstatechange events Cannot read properties of undefined (reading 'currentDirection')
-PASS Closing a PeerConnection should not fire signalingstatechange event
-FAIL signalingstatechange is the first event to fire promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
deleted file mode 100644
index 11c0cf5..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-FAIL setRemoteDescription should trigger ontrack event when the MSID of the stream is is parsed. assert_true: Expect trackEvent.transceiver to be defined and is instance of RTCRtpTransceiver expected true got false
-PASS setRemoteDescription() with m= line of recvonly direction should not trigger track event
-FAIL addTrack() should cause remote connection to fire ontrack when setRemoteDescription() assert_true: Expect trackEvent.transceiver to be defined and is instance of RTCRtpTransceiver expected true got false
-FAIL addTransceiver('video') should cause remote connection to fire ontrack when setRemoteDescription() promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver() with inactive direction should not cause remote connection to fire ontrack when setRemoteDescription() promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS Using offerToReceiveAudio and offerToReceiveVideo should only cause a audio track event to fire, if audio was the only type negotiated
-PASS Using offerToReceiveAudio and offerToReceiveVideo should only cause a video track event to fire, if video was the only type negotiated
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-operations.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-operations.https-expected.txt
deleted file mode 100644
index 3bfc83d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-operations.https-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a testharness.js-based test.
-PASS promiseState helper works
-PASS promiseStateFinal helper works
-FAIL createOffer must detect InvalidStateError synchronously when chain is empty (prerequisite) assert_equals: expected "InvalidStateError" but got "Error"
-FAIL createAnswer must detect InvalidStateError synchronously when chain is empty (prerequisite) assert_equals: promise rejected on same task expected "rejected" but got "pending"
-FAIL SLD(rollback) must detect InvalidStateError synchronously when chain is empty assert_equals: expected "InvalidStateError" but got "OperationError"
-FAIL addIceCandidate must detect InvalidStateError synchronously when chain is empty assert_equals: expected "InvalidStateError" but got "Error"
-FAIL replaceTrack must detect InvalidStateError synchronously when chain is empty and transceiver is stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters must detect InvalidStateError synchronously always when transceiver is stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL pc.getStats must detect InvalidAccessError synchronously always promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL isOperationsChainEmpty detects empty in stable assert_true: Empty to start expected true got false
-FAIL isOperationsChainEmpty detects empty in have-local-offer assert_true: Empty to start expected true got false
-FAIL isOperationsChainEmpty detects empty in have-remote-offer assert_equals: isOperationsChainEmpty is working expected "InvalidStateError" but got "Error"
-PASS createOffer uses operations chain
-FAIL createAnswer uses operations chain assert_equals: isOperationsChainEmpty is working expected "InvalidStateError" but got "Error"
-FAIL setLocalDescription uses operations chain assert_true: Empty before expected true got false
-FAIL setRemoteDescription uses operations chain assert_true: Empty before expected true got false
-FAIL addIceCandidate uses operations chain promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Firing of negotiationneeded does NOT use operations chain promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Negotiationneeded only fires once operations chain is empty promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL replaceTrack uses operations chain promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters does NOT use the operations chain promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL pc.getStats does NOT use the operations chain assert_true: Empty chain expected true got false
-FAIL sender.getStats does NOT use the operations chain promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL receiver.getStats does NOT use the operations chain promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addIceCandidate chains onto SRD, fails before promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Operations queue not vulnerable to recursion by chained negotiationneeded promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Pack operations queue with implicit offer and answer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Negotiate solely by operations queue and signaling state promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https-expected.txt
deleted file mode 100644
index daaaf2d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Perfect negotiation stress glare linear Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL Perfect negotiation stress glare linear with roles reversed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare.https-expected.txt
deleted file mode 100644
index a0a8a55..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Perfect negotiation stress glare Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL Perfect negotiation stress glare with roles reversed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt
deleted file mode 100644
index 637f3d2..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Plan B is not supported assert_not_equals: got disallowed value "plan-b"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
deleted file mode 100644
index 97decd5..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-FAIL addTransceiver - Calling removeTrack when connection is closed should throw InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS addTrack - Calling removeTrack when connection is closed should throw InvalidStateError
-FAIL addTransceiver - Calling removeTrack on different connection that is closed should throw InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS addTrack - Calling removeTrack on different connection that is closed should throw InvalidStateError
-FAIL addTransceiver - Calling removeTrack on different connection should throw InvalidAccessError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS addTrack - Calling removeTrack on different connection should throw InvalidAccessError
-FAIL addTransceiver - Calling removeTrack with valid sender should set sender.track to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS addTrack - Calling removeTrack with valid sender should set sender.track to null
-FAIL Calling removeTrack with currentDirection sendrecv should set direction to recvonly promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling removeTrack with currentDirection sendonly should set direction to inactive promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling removeTrack with currentDirection recvonly should not change direction promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling removeTrack with currentDirection inactive should not change direction promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling removeTrack on a stopped transceiver should be a no-op promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-PASS Calling removeTrack on a null track should have no effect
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https-expected.txt
deleted file mode 100644
index c759bcb1..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce-onnegotiationneeded.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negotiation needed when returning to stable does not fire too early promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
deleted file mode 100644
index 92bfa200..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-restartIce.https-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a testharness.js-based test.
-PASS restartIce() has no effect on a closed peer connection
-FAIL restartIce() does not trigger negotiation ahead of initial negotiation assert_equals: No negotiationneeded event expected (undefined) undefined but got (object) object "[object Event]"
-FAIL restartIce() has no effect on initial negotiation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() fires negotiationneeded after initial negotiation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() causes fresh ufrags promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() retains dtls transports promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() works in have-local-offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() works in initial have-local-offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() works in have-remote-offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() does nothing in initial have-remote-offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() survives remote offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() is satisfied by remote ICE restart promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() trumps {iceRestart: false} promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() survives rollback promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() survives remote offer containing partial restart promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() has no effect on initial negotiation (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() fires negotiationneeded after initial negotiation (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() causes fresh ufrags (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() retains dtls transports (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() works in have-local-offer (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() works in initial have-local-offer (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() works in have-remote-offer (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() does nothing in initial have-remote-offer (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() survives remote offer (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() is satisfied by remote ICE restart (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() trumps {iceRestart: false} (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() survives rollback (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL restartIce() survives remote offer containing partial restart (perfect negotiation) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
deleted file mode 100644
index d953cc0..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL setLocalDescription(offer) with m= section should assign mid to corresponding transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setRemoteDescription(offer) with m= section and no existing transceiver should create corresponding transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setLocalDescription(rollback) should unset transceiver.mid Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setLocalDescription(rollback) should only unset transceiver mids associated with current round Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setRemoteDescription(rollback) should remove newly created transceiver from transceiver list Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setRemoteDescription should set transceiver inactive if its corresponding m section is rejected promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt
deleted file mode 100644
index 90066c2d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS setLocalDescription() with valid answer should succeed
-PASS setLocalDescription() with type answer and null sdp should use lastAnswer generated from createAnswer
-PASS setLocalDescription() with answer not created by own createAnswer() should reject with InvalidModificationError
-FAIL Calling setLocalDescription(answer) from stable state should reject with InvalidModificationError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to set local answer sdp: Called in wrong state: stable" that is not a DOMException InvalidModificationError: property "code" is equal to 0, expected 13
-FAIL Calling setLocalDescription(answer) from have-local-offer state should reject with InvalidModificationError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to set local answer sdp: Called in wrong state: have-local-offer" that is not a DOMException InvalidModificationError: property "code" is equal to 0, expected 13
-FAIL Setting previously generated answer after a call to createOffer should work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setLocalDescription(answer) should update internal state with a queued task, in the right order promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt
deleted file mode 100644
index 48c91d2..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-PASS setLocalDescription with valid offer should succeed
-PASS setLocalDescription with type offer and null sdp should use lastOffer generated from createOffer
-PASS setLocalDescription() with offer not created by own createOffer() should reject with InvalidModificationError
-FAIL Set created offer other than last offer should reject with InvalidModificationError assert_unreached: Should have rejected: undefined Reached unreachable code
-PASS Creating and setting offer multiple times should succeed
-FAIL Setting previously generated offer after a call to createAnswer should work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Negotiation works when there has been a repeated setLocalDescription(offer) promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setLocalDescription(offer) should update internal state with a queued task, in the right order promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https-expected.txt
deleted file mode 100644
index d500a4f8..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-parameterless.https-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a testharness.js-based test.
-PASS Parameterless SLD() in 'stable' goes to 'have-local-offer'
-PASS Parameterless SLD() in 'stable' sets pendingLocalDescription
-FAIL Parameterless SLD() in 'stable' assigns transceiver.mid promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS Parameterless SLD() in 'have-remote-offer' goes to 'stable'
-PASS Parameterless SLD() in 'have-remote-offer' sets currentLocalDescription
-FAIL Parameterless SLD() in 'have-remote-offer' sets transceiver.currentDirection promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Parameterless SLD() uses [[LastCreatedOffer]] if it is still valid assert_true: offerer.pendingLocalDescription.sdp == offer.sdp expected true got false
-FAIL Parameterless SLD() uses [[LastCreatedAnswer]] if it is still valid assert_true: answerer.currentLocalDescription.sdp == answer.sdp expected true got false
-PASS Parameterless SLD() rejects with InvalidStateError if already closed
-PASS Parameterless SLD() never settles if closed while pending
-PASS Parameterless SLD() in a full O/A exchange succeeds
-PASS Parameterless SRD() rejects with TypeError.
-FAIL RTCSessionDescription constructed without type throws TypeError assert_equals: expected "TypeError" but got "ReferenceError"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt
deleted file mode 100644
index 19e80030..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL setLocalDescription(rollback) from have-local-offer state should reset back to stable state promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
-FAIL setLocalDescription(rollback) from stable state should reject with InvalidStateError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
-FAIL setLocalDescription(rollback) after setting answer description should reject with InvalidStateError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
-FAIL setLocalDescription(rollback) should ignore invalid sdp content and succeed promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
-FAIL setLocalDescription(rollback) should update internal state with a queued tassk, in the right order promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
deleted file mode 100644
index 456fb629..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS replaceTrack() sets the track attribute to a new track.
-PASS replaceTrack() sets the track attribute to null.
-PASS replaceTrack() does not set the track synchronously.
-PASS replaceTrack() rejects when the peer connection is closed.
-FAIL replaceTrack() does not reject when invoked after removeTrack(). promise_test: Unhandled rejection with value: object "InvalidModificationError"
-PASS replaceTrack() does not reject after a subsequent removeTrack().
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
deleted file mode 100644
index 85db245..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-This is a testharness.js-based test.
-FAIL setRemoteDescription(rollback) in have-remote-offer state should revert to stable state promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
-FAIL setRemoteDescription(rollback) from stable state should reject with InvalidStateError promise_rejects_dom: function "function() { throw e }" threw object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
-FAIL setRemoteDescription(rollback) should ignore invalid sdp content and succeed promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
-FAIL local offer created before setRemoteDescription(remote offer) then rollback should still be usable promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL local offer created before setRemoteDescription(remote offer) with different transceiver level assignments then rollback should still be usable promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL rollback of a remote offer should remove a transceiver assert_equals: expected 1 but got 0
-FAIL rollback of a remote offer should remove touched transceiver assert_equals: expected 1 but got 0
-FAIL rollback of a remote offer should keep a transceiver assert_equals: expected 1 but got 0
-FAIL rollback of a remote offer should keep a transceiver created by addtrack assert_equals: expected 1 but got 0
-FAIL rollback of a remote offer should keep a transceiver without tracks assert_equals: expected 1 but got 0
-FAIL explicit rollback of local offer should remove transceivers and transport promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL when using addTransceiver, implicit rollback of a local offer should visit stable state, but not fire negotiationneeded until we settle in stable promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL when using addTrack, implicit rollback of a local offer should visit stable state, but not fire negotiationneeded promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: have-local-offer"
-FAIL rollback of a remote offer to negotiated stable state should enable applying of a local offer promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
-FAIL rollback of a local offer to negotiated stable state should enable applying of a remote offer assert_equals: expected 2 but got 0
-FAIL rollback a local offer with audio direction change to negotiated stable state and then add video receiver promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'direction')"
-FAIL two transceivers with same mids promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL onremovetrack fires during remote rollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
-FAIL rollback of a remote offer with stream changes promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL removeTrack() with a sender being rolled back does not crash or throw promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Implicit rollback with only a datachannel works promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-simulcast.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-simulcast.https-expected.txt
deleted file mode 100644
index fb7a34a..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-simulcast.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL createAnswer() attaches to an existing transceiver with a remote simulcast offer assert_equals: Expected exactly one transceiver expected 1 but got 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
deleted file mode 100644
index 4eb476d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-FAIL addTrack() with a track and no stream makes ontrack fire with a track and no stream. assert_equals: No remote stream created. expected 0 but got 1
-PASS addTrack() with a track and a stream makes ontrack fire with a track and a stream.
-PASS ontrack fires before setRemoteDescription resolves.
-PASS addTrack() with two tracks and one stream makes ontrack fire twice with the tracks and shared stream.
-PASS addTrack() for an existing stream makes stream.onaddtrack fire.
-PASS stream.onaddtrack fires before setRemoteDescription resolves.
-FAIL addTrack() with a track and two streams makes ontrack fire with a track and two streams. promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'addTrack' on 'RTCPeerConnection': Adding a track to multiple streams is not supported."
-PASS ontrack's receiver matches getReceivers().
-FAIL removeTrack() does not remove the receiver. assert_equals: Receiver not removed. expected 1 but got 0
-PASS removeTrack() makes stream.onremovetrack fire and the track to be removed from the stream.
-PASS stream.onremovetrack fires before setRemoteDescription resolves.
-PASS removeTrack() makes track.onmute fire and the track to be muted.
-PASS track.onmute fires before setRemoteDescription resolves.
-FAIL removeTrack() twice is safe. promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'removeTrack' on 'RTCPeerConnection': The sender was not created by this peer connection."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
deleted file mode 100644
index 5eff802..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-This is a testharness.js-based test.
-FAIL addTrack: creates a transceiver for the sender assert_true: expected true got false
-FAIL addTrack: "transceiver == {sender,receiver}" assert_array_equals: pc.getTransceivers() equals [transceiver] lengths differ, expected array [null] length 1, got [] length 0
-FAIL addTrack: transceiver.sender is associated with the track promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'sender')"
-FAIL addTrack: transceiver.receiver has its own track promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'receiver')"
-FAIL addTrack: transceiver.receiver's track is muted promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'receiver')"
-FAIL addTrack: transceiver is not associated with an m-section promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'mid')"
-FAIL addTrack: transceiver is not stopped promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'stopped')"
-FAIL addTrack: transceiver's direction is sendrecv promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'direction')"
-FAIL addTrack: transceiver's currentDirection is null promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'currentDirection')"
-FAIL setLocalDescription(offer): transceiver gets associated with an m-section promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'mid')"
-FAIL setLocalDescription(offer): transceiver.mid matches the offer SDP promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'mid')"
-PASS setRemoteDescription(offer): ontrack fires with a track
-PASS setRemoteDescription(offer): ontrack's stream.id is the same as stream.id
-FAIL setRemoteDescription(offer): ontrack fires with a transceiver. assert_true: trackEvent.transceiver instanceof RTCRtpTransceiver expected true got false
-FAIL setRemoteDescription(offer): transceiver.mid is the same on both ends promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'mid')"
-FAIL setRemoteDescription(offer): "transceiver == {sender,receiver}" assert_array_equals: pc2.getTransceivers() equals [transceiver] lengths differ, expected array [null] length 1, got [] length 0
-FAIL setRemoteDescription(offer): transceiver.direction is recvonly promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'direction')"
-FAIL setRemoteDescription(offer): transceiver.currentDirection is null promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'currentDirection')"
-FAIL setRemoteDescription(offer): transceiver.stopped is false promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'stopped')"
-FAIL setLocalDescription(answer): transceiver.currentDirection is recvonly promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'currentDirection')"
-FAIL setLocalDescription(answer): transceiver.currentDirection is sendonly promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'currentDirection')"
-FAIL addTransceiver(track): creates a transceiver for the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(track): "transceiver == {sender,receiver}" promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(track, init): initialize direction to inactive promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(track, init): initialize sendEncodings[0].active to false promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(0 streams): ontrack fires with no stream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(1 stream): ontrack fires with corresponding stream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver(2 streams): ontrack fires with corresponding two streams promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTrack(0 streams): ontrack fires with no stream assert_equals: trackEvent.streams.length == 0 expected 0 but got 1
-PASS addTrack(1 stream): ontrack fires with corresponding stream
-FAIL addTrack(2 streams): ontrack fires with corresponding two streams promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'addTrack' on 'RTCPeerConnection': Adding a track to multiple streams is not supported."
-FAIL addTransceiver('audio'): creates a transceiver with direction sendrecv promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver('audio'): transceiver.receiver.track.kind == 'audio' promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver('video'): transceiver.receiver.track.kind == 'video' promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver('audio'): transceiver.sender.track == null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver('audio'): transceiver.currentDirection is null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver('audio'): transceiver.stopped is false promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTrack reuses reusable transceivers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver does not reuse reusable transceivers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Can setup two-way call using a single transceiver promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'direction')"
-FAIL Closing the PC stops the transceivers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Changing transceiver direction to 'sendrecv' makes ontrack fire promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of null (setting 'direction')"
-FAIL transceiver.sender.track does not revert to an old state promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL transceiver.direction does not revert to an old state promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
deleted file mode 100644
index 31d0ffe4..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters() with codec.payloadType modified should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with codec.mimeType modified should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with codec.clockRate modified should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with codec.channels modified should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with codec.sdpFmtpLine modified should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with new codecs inserted should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
deleted file mode 100644
index a6f5b49..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters with degradationPreference set should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setParameters with degradationPreference unset should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
deleted file mode 100644
index 983cc21..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-FAIL addTransceiver() with undefined sendEncodings should have default encoding parameter with active set to true promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL addTransceiver() with empty list sendEncodings should have default encoding parameter with active set to true promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sender.getParameters() should return sendEncodings set by addTransceiver() promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sender.setParameters() with mismatch number of encodings should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sender.setParameters() with encodings unset should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.rid field should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with encoding.scaleResolutionDownBy field set to less than 1.0 should reject with RangeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with encoding.scaleResolutionDownBy field set to greater than 1.0 should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.active should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.active should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.maxBitrate should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.maxBitrate should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.scaleResolutionDownBy should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with modified encoding.scaleResolutionDownBy should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
deleted file mode 100644
index dd7028e3e..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters() with modified headerExtensions should reject with InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
deleted file mode 100644
index 3d6c127..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters() with modified rtcp.cname should reject with InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setParameters() with modified rtcp.reducedSize should reject with InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
deleted file mode 100644
index 8bf68c40..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL sender.getParameters() should return different transaction IDs for each call promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sender.setParameters() with transaction ID different from last getParameters() should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sender.setParameters() with transaction ID unset should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() twice with the same parameters should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setParameters() with parameters older than last getParameters() should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
deleted file mode 100644
index c3193c64..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL getParameters() with audio receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL getParameters() with video receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL getParameters() with simulcast video receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
deleted file mode 100644
index 8822646..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL receiver.getStats() via addTrack should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
-FAIL receiver.getStats() should work on a stopped transceiver promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL receiver.getStats() should work with a closed PeerConnection assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-encode-same-track-twice.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-encode-same-track-twice.https-expected.txt
deleted file mode 100644
index d361cb82..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-encode-same-track-twice.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Two RTCRtpSenders encoding the same track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
deleted file mode 100644
index 0a6f0596..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL sender.getStats() via addTrack should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.senderId to be string expected "string" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
deleted file mode 100644
index d2e40876b..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is a testharness.js-based test.
-FAIL Calling replaceTrack on closed connection should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack with track of different kind should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack on stopped sender should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack on sender with null track and not set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack on sender not set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack(null) on sender not set to session description should resolve with sender.track set to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack(null) on sender set to session description should resolve with sender.track set to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack on sender with stopped track and and set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Calling replaceTrack on sender with similar track and and set to session description should resolve with sender.track set to new track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS ReplaceTrack transmits the new track not the old track
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
deleted file mode 100644
index 5172420..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters() when transceiver is stopped should reject with InvalidStateError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setStreams.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setStreams.https-expected.txt
deleted file mode 100644
index 3f42baa..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setStreams.https-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL setStreams causes streams to be reported via ontrack on callee promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'setStreams' on 'RTCRtpSender': This operation is only supported in 'unified-plan'."
-FAIL setStreams can be used to reconstruct a stream with a track on the remote side promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'setStreams' on 'RTCRtpSender': This operation is only supported in 'unified-plan'."
-FAIL Adding streams and changing direction causes new streams to be reported via ontrack on callee promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Adding streams to an active transceiver causes new streams to be reported via ontrack on callee promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setStreams() fires InvalidStateError on a closed peer connection. Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
deleted file mode 100644
index 66dbc45..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-PASS RTCRtpSender.transport is null when unconnected
-FAIL RTCRtpSender/receiver.transport has a value when connected assert_not_equals: got disallowed value null
-FAIL RTCRtpSender/receiver.transport at the right time, with bundle policy balanced assert_not_equals: got disallowed value null
-FAIL RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy balanced assert_not_equals: got disallowed value null
-FAIL RTCRtpSender/receiver.transport at the right time, with bundle policy max-bundle assert_not_equals: got disallowed value null
-FAIL RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy max-bundle assert_not_equals: got disallowed value null
-FAIL RTCRtpSender/receiver.transport at the right time, with bundle policy max-compat assert_not_equals: got disallowed value null
-FAIL RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy max-compat assert_not_equals: got disallowed value null
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender.https-expected.txt
deleted file mode 100644
index 01f8aa1..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Video sender @dtmf is null Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
deleted file mode 100644
index 1f4349c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL setting direction should change transceiver.direction Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setting direction with same direction should have no effect Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setting direction should change transceiver.direction independent of transceiver.currentDirection Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
deleted file mode 100644
index 42ffe99..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This is a testharness.js-based test.
-FAIL setCodecPreferences() on audio transceiver with codecs returned from RTCRtpSender.getCapabilities('audio') should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with both sender receiver codecs combined should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences([]) should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with reordered codecs should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with only VP8 should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with only H264 should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() should allow setting H264 as first codec promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setCodecPreferences() should allow setting VP8 as first codec promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with user defined codec with invalid mimeType should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with user defined codec should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with modified codec clock rate should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with modified codec channel count should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with modified codec parameters should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidModificationError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL setCodecPreferences() modifies the order of audio codecs in createOffer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL setCodecPreferences() modifies the order of video codecs in createOffer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
deleted file mode 100644
index 483bec2..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-FAIL A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL A transceiver added and stopped should not crash when getting receiver's transport promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL A stopped sendonly transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL A stopped inactive transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL If a transceiver is stopped locally, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL If a transceiver is stopped remotely, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL If a transceiver is stopped, transceivers should end up in state stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stopping.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stopping.https-expected.txt
deleted file mode 100644
index b970e97..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stopping.https-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-FAIL [audio] Locally stopped transceiver goes from stopping to stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Locally stopping a transceiver ends the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Remotely stopping a transceiver ends the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Remotely stopped transceiver goes directly to stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Rollback when transceiver is not removed does not end track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Rollback when removing transceiver does end the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Rollback when removing transceiver makes it stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [audio] Glare when transceiver is not removed does not end track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: have-local-offer"
-FAIL [video] Locally stopped transceiver goes from stopping to stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Locally stopping a transceiver ends the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Remotely stopping a transceiver ends the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Remotely stopped transceiver goes directly to stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Rollback when transceiver is not removed does not end track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Rollback when removing transceiver does end the track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Rollback when removing transceiver makes it stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] Glare when transceiver is not removed does not end track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: have-local-offer"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
deleted file mode 100644
index 58e77a7e..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Cannot read properties of undefined (reading 'receiver')
-FAIL checkAddTransceiverNoTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverWithTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverWithAddTrack assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:{}}},{currentDirection:null,direction:\"sendrecv\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:{}}}]" but got "[]"
-FAIL checkAddTransceiverWithDirection promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS checkMsidNoTrackId
-FAIL checkAddTransceiverWithSetRemoteOfferSending promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverWithSetRemoteOfferNoSend promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverBadKind assert_true: addTransceiver("foo") throws a TypeError expected true got false
-FAIL checkNoMidOffer assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",receiver:{track:{kind:\"audio\"}},sender:{track:null}}]" but got "[]"
-FAIL checkNoMidAnswer assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{kind:\"audio\"}}}]" but got "[]"
-FAIL checkSetDirection promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkCurrentDirection assert_equals: expected "[{currentDirection:null}]" but got "[]"
-FAIL checkSendrecvWithNoSendTrack promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkSendrecvWithTracklessStream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverNoTrackDoesntPair promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverWithTrackDoesntPair promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverThenReplaceTrackDoesntPair promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverThenAddTrackPairs promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTrackPairs promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkReplaceTrackNullDoesntPreventPairing promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkRemoveAndReadd assert_equals: expected "[{direction:\"recvonly\",sender:{track:null}},{direction:\"sendrecv\",sender:{track:{}}}]" but got "[]"
-FAIL checkAddTrackExistingTransceiverThenRemove promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkRemoveTrackNegotiation promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of undefined (setting 'direction')"
-FAIL checkMute promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'receiver')"
-FAIL checkStop promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkStopAfterCreateOffer promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkStopAfterSetLocalOffer promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkStopAfterSetRemoteOffer promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkStopAfterCreateAnswer promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkStopAfterSetLocalAnswer promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'receiver')"
-FAIL checkStopAfterClose assert_equals: Stopping a transceiver on a closed PC should throw. throws InvalidStateError expected "InvalidStateError" but got "TypeError"
-FAIL checkLocalRollback assert_equals: expected "[{currentDirection:null,direction:\"sendrecv\",receiver:{track:{kind:\"audio\"}},sender:{track:{}}}]" but got "[]"
-FAIL checkRollbackAndSetRemoteOfferWithDifferentType promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Rollback not supported in Plan B"
-FAIL checkRemoteRollback promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote rollback sdp: Rollback not supported in Plan B"
-FAIL checkMsectionReuse promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'mid')"
-FAIL checkStopAfterCreateOfferWithReusedMsection promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkAddIceCandidateToStoppedTransceiver promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-FAIL checkBundleTagRejected promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'stop')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt
deleted file mode 100644
index 078a911..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-constructor-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS setRemoteDescription() with answer not containing data media should not initialize pc.sctp
-PASS setLocalDescription() with answer not containing data media should not initialize pc.sctp
-FAIL setRemoteDescription() with answer containing data media should initialize pc.sctp assert_not_equals: RTCSctpTransport must be available got disallowed value null
-FAIL setLocalDescription() with answer containing data media should initialize pc.sctp assert_not_equals: RTCSctpTransport must be available got disallowed value null
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-events-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-events-expected.txt
deleted file mode 100644
index ef768a3..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-events-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Cannot read properties of null (reading 'state')
-FAIL SctpTransport objects are created at appropriate times assert_not_equals: got disallowed value null
-FAIL SctpTransport reaches connected and closed state promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'state')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-maxChannels-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-maxChannels-expected.txt
deleted file mode 100644
index 207b13c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-maxChannels-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Cannot read properties of null (reading 'state')
-FAIL An unconnected peerconnection must not have maxChannels set assert_not_equals: RTCSctpTransport must be available got disallowed value null
-FAIL maxChannels gets instantiated after connecting promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'maxChannels')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt
deleted file mode 100644
index a5f7c3e..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCSctpTransport-maxMessageSize-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-FAIL Determine the local side send limitation (canSendSize) by offering a max-message-size of 0 assert_not_equals: RTCSctpTransport must be available got disallowed value null
-FAIL Remote offer SDP missing max-message-size attribute assert_not_equals: canSendSize needs to be determined got disallowed value null
-FAIL max-message-size with a (non-zero) value provided by the remote peer assert_not_equals: canSendSize needs to be determined got disallowed value null
-FAIL Renegotiate max-message-size with various values provided by the remote peer assert_not_equals: canSendSize needs to be determined got disallowed value null
-FAIL max-message-size with a (non-zero) value larger than canSendSize provided by the remote peer assert_not_equals: canSendSize needs to be determined got disallowed value null
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
deleted file mode 100644
index 3f47439c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-FAIL new RTCTrackEvent() with valid receiver, track, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL new RTCTrackEvent() with valid receiver, track, streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL new RTCTrackEvent() with valid receiver, track, multiple streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL new RTCTrackEvent() with unrelated receiver, track, streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL new RTCTrackEvent() with no transceiver should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL new RTCTrackEvent() with no track should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL new RTCTrackEvent() with no receiver should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RollbackEvents.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RollbackEvents.https-expected.txt
deleted file mode 100644
index c566f48a..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RollbackEvents.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-FAIL [audio] Track with stream: removal due to disassociation in rollback and then add it back again assert_equals: expected 1 but got 0
-FAIL [audio] Track without stream: removal due to disassociation in rollback and then add it back assert_equals: expected 1 but got 0
-FAIL [audio] Track with stream: removal due to direction changing and then add back using rollback promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'direction')"
-FAIL [audio] Track without stream: removal due to direction changing and then add back using rollback promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'direction')"
-FAIL [video] Track with stream: removal due to disassociation in rollback and then add it back again assert_equals: expected 1 but got 0
-FAIL [video] Track without stream: removal due to disassociation in rollback and then add it back assert_equals: expected 1 but got 0
-FAIL [video] Track with stream: removal due to direction changing and then add back using rollback promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'direction')"
-FAIL [video] Track without stream: removal due to direction changing and then add back using rollback promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'direction')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
deleted file mode 100644
index ed5eee3..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ /dev/null
@@ -1,507 +0,0 @@
-This is a testharness.js-based test.
-Found 497 tests; 414 PASS, 83 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Test driver for asyncInitCertificate
-FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: Error: assert_true: Expect pc.sctp to be instance of RTCSctpTransport expected true got false Reached unreachable code
-PASS Test driver for asyncInitMediaStreamTrack
-PASS Partial dictionary RTCOfferOptions: original dictionary defined
-PASS Partial dictionary RTCOfferOptions: member names are unique
-PASS Partial interface RTCPeerConnection: original interface defined
-PASS Partial interface RTCPeerConnection: member names are unique
-PASS Partial interface RTCPeerConnection[2]: original interface defined
-PASS Partial interface RTCPeerConnection[2]: member names are unique
-PASS Partial interface RTCPeerConnection[3]: original interface defined
-PASS Partial interface RTCPeerConnection[3]: member names are unique
-PASS Partial interface RTCRtpSender: original interface defined
-PASS Partial interface RTCRtpSender: member names are unique
-PASS Partial interface RTCPeerConnection[4]: original interface defined
-PASS Partial interface RTCPeerConnection[4]: member names are unique
-PASS RTCPeerConnection interface: existence and properties of interface object
-PASS RTCPeerConnection interface object length
-PASS RTCPeerConnection interface object name
-PASS RTCPeerConnection interface: existence and properties of interface prototype object
-PASS RTCPeerConnection interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCPeerConnection interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCPeerConnection interface: operation createOffer(optional RTCOfferOptions)
-PASS RTCPeerConnection interface: operation createAnswer(optional RTCAnswerOptions)
-PASS RTCPeerConnection interface: operation setLocalDescription(optional RTCLocalSessionDescriptionInit)
-PASS RTCPeerConnection interface: attribute localDescription
-PASS RTCPeerConnection interface: attribute currentLocalDescription
-PASS RTCPeerConnection interface: attribute pendingLocalDescription
-PASS RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescriptionInit)
-PASS RTCPeerConnection interface: attribute remoteDescription
-PASS RTCPeerConnection interface: attribute currentRemoteDescription
-PASS RTCPeerConnection interface: attribute pendingRemoteDescription
-PASS RTCPeerConnection interface: operation addIceCandidate(optional RTCIceCandidateInit)
-PASS RTCPeerConnection interface: attribute signalingState
-PASS RTCPeerConnection interface: attribute iceGatheringState
-PASS RTCPeerConnection interface: attribute iceConnectionState
-PASS RTCPeerConnection interface: attribute connectionState
-PASS RTCPeerConnection interface: attribute canTrickleIceCandidates
-PASS RTCPeerConnection interface: operation restartIce()
-PASS RTCPeerConnection interface: operation getConfiguration()
-FAIL RTCPeerConnection interface: operation setConfiguration(optional RTCConfiguration) assert_equals: property has wrong .length expected 0 but got 1
-PASS RTCPeerConnection interface: operation close()
-PASS RTCPeerConnection interface: attribute onnegotiationneeded
-PASS RTCPeerConnection interface: attribute onicecandidate
-PASS RTCPeerConnection interface: attribute onicecandidateerror
-PASS RTCPeerConnection interface: attribute onsignalingstatechange
-PASS RTCPeerConnection interface: attribute oniceconnectionstatechange
-PASS RTCPeerConnection interface: attribute onicegatheringstatechange
-PASS RTCPeerConnection interface: attribute onconnectionstatechange
-PASS RTCPeerConnection interface: operation createOffer(RTCSessionDescriptionCallback, RTCPeerConnectionErrorCallback, optional RTCOfferOptions)
-PASS RTCPeerConnection interface: operation setLocalDescription(RTCLocalSessionDescriptionInit, VoidFunction, RTCPeerConnectionErrorCallback)
-PASS RTCPeerConnection interface: operation createAnswer(RTCSessionDescriptionCallback, RTCPeerConnectionErrorCallback)
-PASS RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescriptionInit, VoidFunction, RTCPeerConnectionErrorCallback)
-PASS RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidateInit, VoidFunction, RTCPeerConnectionErrorCallback)
-PASS RTCPeerConnection interface: operation generateCertificate(AlgorithmIdentifier)
-PASS RTCPeerConnection interface: operation getSenders()
-PASS RTCPeerConnection interface: operation getReceivers()
-PASS RTCPeerConnection interface: operation getTransceivers()
-PASS RTCPeerConnection interface: operation addTrack(MediaStreamTrack, MediaStream...)
-PASS RTCPeerConnection interface: operation removeTrack(RTCRtpSender)
-PASS RTCPeerConnection interface: operation addTransceiver((MediaStreamTrack or DOMString), optional RTCRtpTransceiverInit)
-PASS RTCPeerConnection interface: attribute ontrack
-PASS RTCPeerConnection interface: attribute sctp
-PASS RTCPeerConnection interface: operation createDataChannel(USVString, optional RTCDataChannelInit)
-PASS RTCPeerConnection interface: attribute ondatachannel
-PASS RTCPeerConnection interface: operation getStats(optional MediaStreamTrack?)
-PASS RTCPeerConnection must be primary interface of new RTCPeerConnection()
-PASS Stringification of new RTCPeerConnection()
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createOffer(optional RTCOfferOptions)" with the proper type
-PASS RTCPeerConnection interface: calling createOffer(optional RTCOfferOptions) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createAnswer(optional RTCAnswerOptions)" with the proper type
-PASS RTCPeerConnection interface: calling createAnswer(optional RTCAnswerOptions) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setLocalDescription(optional RTCLocalSessionDescriptionInit)" with the proper type
-PASS RTCPeerConnection interface: calling setLocalDescription(optional RTCLocalSessionDescriptionInit) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "localDescription" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentLocalDescription" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingLocalDescription" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setRemoteDescription(RTCSessionDescriptionInit)" with the proper type
-PASS RTCPeerConnection interface: calling setRemoteDescription(RTCSessionDescriptionInit) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "remoteDescription" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "currentRemoteDescription" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "pendingRemoteDescription" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addIceCandidate(optional RTCIceCandidateInit)" with the proper type
-PASS RTCPeerConnection interface: calling addIceCandidate(optional RTCIceCandidateInit) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "signalingState" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "iceGatheringState" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "iceConnectionState" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "connectionState" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "canTrickleIceCandidates" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "restartIce()" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getConfiguration()" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setConfiguration(optional RTCConfiguration)" with the proper type
-PASS RTCPeerConnection interface: calling setConfiguration(optional RTCConfiguration) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "close()" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "onnegotiationneeded" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "onicecandidate" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "onicecandidateerror" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "onsignalingstatechange" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "oniceconnectionstatechange" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "onicegatheringstatechange" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "onconnectionstatechange" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createOffer(RTCSessionDescriptionCallback, RTCPeerConnectionErrorCallback, optional RTCOfferOptions)" with the proper type
-PASS RTCPeerConnection interface: calling createOffer(RTCSessionDescriptionCallback, RTCPeerConnectionErrorCallback, optional RTCOfferOptions) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setLocalDescription(RTCLocalSessionDescriptionInit, VoidFunction, RTCPeerConnectionErrorCallback)" with the proper type
-PASS RTCPeerConnection interface: calling setLocalDescription(RTCLocalSessionDescriptionInit, VoidFunction, RTCPeerConnectionErrorCallback) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createAnswer(RTCSessionDescriptionCallback, RTCPeerConnectionErrorCallback)" with the proper type
-PASS RTCPeerConnection interface: calling createAnswer(RTCSessionDescriptionCallback, RTCPeerConnectionErrorCallback) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "setRemoteDescription(RTCSessionDescriptionInit, VoidFunction, RTCPeerConnectionErrorCallback)" with the proper type
-PASS RTCPeerConnection interface: calling setRemoteDescription(RTCSessionDescriptionInit, VoidFunction, RTCPeerConnectionErrorCallback) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addIceCandidate(RTCIceCandidateInit, VoidFunction, RTCPeerConnectionErrorCallback)" with the proper type
-PASS RTCPeerConnection interface: calling addIceCandidate(RTCIceCandidateInit, VoidFunction, RTCPeerConnectionErrorCallback) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "generateCertificate(AlgorithmIdentifier)" with the proper type
-PASS RTCPeerConnection interface: calling generateCertificate(AlgorithmIdentifier) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getSenders()" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getReceivers()" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getTransceivers()" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTrack(MediaStreamTrack, MediaStream...)" with the proper type
-PASS RTCPeerConnection interface: calling addTrack(MediaStreamTrack, MediaStream...) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "removeTrack(RTCRtpSender)" with the proper type
-PASS RTCPeerConnection interface: calling removeTrack(RTCRtpSender) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTransceiver((MediaStreamTrack or DOMString), optional RTCRtpTransceiverInit)" with the proper type
-PASS RTCPeerConnection interface: calling addTransceiver((MediaStreamTrack or DOMString), optional RTCRtpTransceiverInit) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ontrack" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "sctp" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "createDataChannel(USVString, optional RTCDataChannelInit)" with the proper type
-PASS RTCPeerConnection interface: calling createDataChannel(USVString, optional RTCDataChannelInit) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ondatachannel" with the proper type
-PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getStats(optional MediaStreamTrack?)" with the proper type
-PASS RTCPeerConnection interface: calling getStats(optional MediaStreamTrack?) on new RTCPeerConnection() with too few arguments must throw TypeError
-PASS RTCSessionDescription interface: existence and properties of interface object
-FAIL RTCSessionDescription interface object length assert_equals: wrong value for RTCSessionDescription.length expected 1 but got 0
-PASS RTCSessionDescription interface object name
-PASS RTCSessionDescription interface: existence and properties of interface prototype object
-PASS RTCSessionDescription interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCSessionDescription interface: existence and properties of interface prototype object's @@unscopables property
-FAIL RTCSessionDescription interface: attribute type assert_equals: setter must be undefined for readonly attributes expected (undefined) undefined but got (function) function "function set type() { [native code] }"
-FAIL RTCSessionDescription interface: attribute sdp assert_equals: setter must be undefined for readonly attributes expected (undefined) undefined but got (function) function "function set sdp() { [native code] }"
-PASS RTCSessionDescription interface: operation toJSON()
-PASS RTCSessionDescription must be primary interface of new RTCSessionDescription({ type: 'offer' })
-PASS Stringification of new RTCSessionDescription({ type: 'offer' })
-PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "type" with the proper type
-PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "sdp" with the proper type
-PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "toJSON()" with the proper type
-PASS RTCSessionDescription interface: default toJSON operation on new RTCSessionDescription({ type: 'offer' })
-PASS RTCIceCandidate interface: existence and properties of interface object
-PASS RTCIceCandidate interface object length
-PASS RTCIceCandidate interface object name
-PASS RTCIceCandidate interface: existence and properties of interface prototype object
-PASS RTCIceCandidate interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCIceCandidate interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCIceCandidate interface: attribute candidate
-PASS RTCIceCandidate interface: attribute sdpMid
-PASS RTCIceCandidate interface: attribute sdpMLineIndex
-PASS RTCIceCandidate interface: attribute foundation
-PASS RTCIceCandidate interface: attribute component
-PASS RTCIceCandidate interface: attribute priority
-PASS RTCIceCandidate interface: attribute address
-PASS RTCIceCandidate interface: attribute protocol
-PASS RTCIceCandidate interface: attribute port
-PASS RTCIceCandidate interface: attribute type
-PASS RTCIceCandidate interface: attribute tcpType
-PASS RTCIceCandidate interface: attribute relatedAddress
-PASS RTCIceCandidate interface: attribute relatedPort
-PASS RTCIceCandidate interface: attribute usernameFragment
-PASS RTCIceCandidate interface: operation toJSON()
-PASS RTCIceCandidate must be primary interface of new RTCIceCandidate({ sdpMid: 1 })
-PASS Stringification of new RTCIceCandidate({ sdpMid: 1 })
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "candidate" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "sdpMid" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "sdpMLineIndex" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "foundation" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "component" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "priority" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "address" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "protocol" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "port" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "type" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "tcpType" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "relatedAddress" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "relatedPort" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "usernameFragment" with the proper type
-PASS RTCIceCandidate interface: new RTCIceCandidate({ sdpMid: 1 }) must inherit property "toJSON()" with the proper type
-PASS RTCIceCandidate interface: toJSON operation on new RTCIceCandidate({ sdpMid: 1 })
-PASS RTCPeerConnectionIceEvent interface: existence and properties of interface object
-PASS RTCPeerConnectionIceEvent interface object length
-PASS RTCPeerConnectionIceEvent interface object name
-PASS RTCPeerConnectionIceEvent interface: existence and properties of interface prototype object
-PASS RTCPeerConnectionIceEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCPeerConnectionIceEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCPeerConnectionIceEvent interface: attribute candidate
-FAIL RTCPeerConnectionIceEvent interface: attribute url assert_true: The prototype object must have a property "url" expected true got false
-PASS RTCPeerConnectionIceEvent must be primary interface of new RTCPeerConnectionIceEvent('ice')
-PASS Stringification of new RTCPeerConnectionIceEvent('ice')
-PASS RTCPeerConnectionIceEvent interface: new RTCPeerConnectionIceEvent('ice') must inherit property "candidate" with the proper type
-FAIL RTCPeerConnectionIceEvent interface: new RTCPeerConnectionIceEvent('ice') must inherit property "url" with the proper type assert_inherits: property "url" not found in prototype chain
-PASS RTCPeerConnectionIceErrorEvent interface: existence and properties of interface object
-PASS RTCPeerConnectionIceErrorEvent interface object length
-PASS RTCPeerConnectionIceErrorEvent interface object name
-PASS RTCPeerConnectionIceErrorEvent interface: existence and properties of interface prototype object
-PASS RTCPeerConnectionIceErrorEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCPeerConnectionIceErrorEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCPeerConnectionIceErrorEvent interface: attribute address
-PASS RTCPeerConnectionIceErrorEvent interface: attribute port
-PASS RTCPeerConnectionIceErrorEvent interface: attribute url
-PASS RTCPeerConnectionIceErrorEvent interface: attribute errorCode
-PASS RTCPeerConnectionIceErrorEvent interface: attribute errorText
-PASS RTCPeerConnectionIceErrorEvent must be primary interface of new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 });
-PASS Stringification of new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 });
-PASS RTCPeerConnectionIceErrorEvent interface: new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 }); must inherit property "address" with the proper type
-PASS RTCPeerConnectionIceErrorEvent interface: new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 }); must inherit property "port" with the proper type
-PASS RTCPeerConnectionIceErrorEvent interface: new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 }); must inherit property "url" with the proper type
-PASS RTCPeerConnectionIceErrorEvent interface: new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 }); must inherit property "errorCode" with the proper type
-PASS RTCPeerConnectionIceErrorEvent interface: new RTCPeerConnectionIceErrorEvent('ice-error', { port: 0, errorCode: 701 }); must inherit property "errorText" with the proper type
-PASS RTCCertificate interface: existence and properties of interface object
-PASS RTCCertificate interface object length
-PASS RTCCertificate interface object name
-PASS RTCCertificate interface: existence and properties of interface prototype object
-PASS RTCCertificate interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCCertificate interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCCertificate interface: attribute expires
-PASS RTCCertificate interface: operation getFingerprints()
-PASS RTCCertificate must be primary interface of idlTestObjects.certificate
-PASS Stringification of idlTestObjects.certificate
-PASS RTCCertificate interface: idlTestObjects.certificate must inherit property "expires" with the proper type
-PASS RTCCertificate interface: idlTestObjects.certificate must inherit property "getFingerprints()" with the proper type
-PASS RTCRtpSender interface: existence and properties of interface object
-PASS RTCRtpSender interface object length
-PASS RTCRtpSender interface object name
-PASS RTCRtpSender interface: existence and properties of interface prototype object
-PASS RTCRtpSender interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCRtpSender interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCRtpSender interface: attribute track
-PASS RTCRtpSender interface: attribute transport
-PASS RTCRtpSender interface: operation getCapabilities(DOMString)
-PASS RTCRtpSender interface: operation setParameters(RTCRtpSendParameters)
-PASS RTCRtpSender interface: operation getParameters()
-PASS RTCRtpSender interface: operation replaceTrack(MediaStreamTrack?)
-PASS RTCRtpSender interface: operation setStreams(MediaStream...)
-PASS RTCRtpSender interface: operation getStats()
-PASS RTCRtpSender interface: attribute dtmf
-FAIL RTCRtpSender must be primary interface of new RTCPeerConnection().addTransceiver('audio').sender assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Stringification of new RTCPeerConnection().addTransceiver('audio').sender assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "transport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getCapabilities(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setParameters(RTCRtpSendParameters)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: calling setParameters(RTCRtpSendParameters) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getParameters()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "replaceTrack(MediaStreamTrack?)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: calling replaceTrack(MediaStreamTrack?) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setStreams(MediaStream...)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: calling setStreams(MediaStream...) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getStats()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "dtmf" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS RTCRtpReceiver interface: existence and properties of interface object
-PASS RTCRtpReceiver interface object length
-PASS RTCRtpReceiver interface object name
-PASS RTCRtpReceiver interface: existence and properties of interface prototype object
-PASS RTCRtpReceiver interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCRtpReceiver interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCRtpReceiver interface: attribute track
-PASS RTCRtpReceiver interface: attribute transport
-PASS RTCRtpReceiver interface: operation getCapabilities(DOMString)
-PASS RTCRtpReceiver interface: operation getParameters()
-PASS RTCRtpReceiver interface: operation getContributingSources()
-PASS RTCRtpReceiver interface: operation getSynchronizationSources()
-PASS RTCRtpReceiver interface: operation getStats()
-FAIL RTCRtpReceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio').receiver assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Stringification of new RTCPeerConnection().addTransceiver('audio').receiver assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "transport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getCapabilities(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').receiver with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getParameters()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getContributingSources()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getSynchronizationSources()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getStats()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS RTCRtpTransceiver interface: existence and properties of interface object
-PASS RTCRtpTransceiver interface object length
-PASS RTCRtpTransceiver interface object name
-PASS RTCRtpTransceiver interface: existence and properties of interface prototype object
-PASS RTCRtpTransceiver interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCRtpTransceiver interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCRtpTransceiver interface: attribute mid
-PASS RTCRtpTransceiver interface: attribute sender
-PASS RTCRtpTransceiver interface: attribute receiver
-PASS RTCRtpTransceiver interface: attribute direction
-PASS RTCRtpTransceiver interface: attribute currentDirection
-PASS RTCRtpTransceiver interface: operation stop()
-PASS RTCRtpTransceiver interface: operation setCodecPreferences(sequence<RTCRtpCodecCapability>)
-FAIL RTCRtpTransceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio') assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Stringification of new RTCPeerConnection().addTransceiver('audio') assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "mid" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "sender" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "receiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "direction" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "currentDirection" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stop()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "setCodecPreferences(sequence<RTCRtpCodecCapability>)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCRtpTransceiver interface: calling setCodecPreferences(sequence<RTCRtpCodecCapability>) on new RTCPeerConnection().addTransceiver('audio') with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS RTCDtlsTransport interface: existence and properties of interface object
-PASS RTCDtlsTransport interface object length
-PASS RTCDtlsTransport interface object name
-PASS RTCDtlsTransport interface: existence and properties of interface prototype object
-PASS RTCDtlsTransport interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCDtlsTransport interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCDtlsTransport interface: attribute iceTransport
-PASS RTCDtlsTransport interface: attribute state
-PASS RTCDtlsTransport interface: operation getRemoteCertificates()
-PASS RTCDtlsTransport interface: attribute onstatechange
-PASS RTCDtlsTransport interface: attribute onerror
-FAIL RTCDtlsTransport must be primary interface of idlTestObjects.dtlsTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL Stringification of idlTestObjects.dtlsTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "iceTransport" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "state" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-PASS RTCIceTransport interface: existence and properties of interface object
-PASS RTCIceTransport interface object length
-PASS RTCIceTransport interface object name
-PASS RTCIceTransport interface: existence and properties of interface prototype object
-PASS RTCIceTransport interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCIceTransport interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCIceTransport interface: attribute role
-FAIL RTCIceTransport interface: attribute component assert_true: The prototype object must have a property "component" expected true got false
-PASS RTCIceTransport interface: attribute state
-PASS RTCIceTransport interface: attribute gatheringState
-PASS RTCIceTransport interface: operation getLocalCandidates()
-PASS RTCIceTransport interface: operation getRemoteCandidates()
-PASS RTCIceTransport interface: operation getSelectedCandidatePair()
-PASS RTCIceTransport interface: operation getLocalParameters()
-PASS RTCIceTransport interface: operation getRemoteParameters()
-PASS RTCIceTransport interface: attribute onstatechange
-PASS RTCIceTransport interface: attribute ongatheringstatechange
-PASS RTCIceTransport interface: attribute onselectedcandidatepairchange
-FAIL RTCIceTransport must be primary interface of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL Stringification of idlTestObjects.iceTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "role" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "component" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "state" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "gatheringState" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getLocalCandidates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getRemoteCandidates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getSelectedCandidatePair()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getLocalParameters()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "getRemoteParameters()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "ongatheringstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: idlTestObjects.iceTransport must inherit property "onselectedcandidatepairchange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-PASS RTCTrackEvent interface: existence and properties of interface object
-PASS RTCTrackEvent interface object length
-PASS RTCTrackEvent interface object name
-PASS RTCTrackEvent interface: existence and properties of interface prototype object
-PASS RTCTrackEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCTrackEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCTrackEvent interface: attribute receiver
-PASS RTCTrackEvent interface: attribute track
-PASS RTCTrackEvent interface: attribute streams
-PASS RTCTrackEvent interface: attribute transceiver
-FAIL RTCTrackEvent must be primary interface of initTrackEvent() assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Stringification of initTrackEvent() assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "receiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS RTCSctpTransport interface: existence and properties of interface object
-PASS RTCSctpTransport interface object length
-PASS RTCSctpTransport interface object name
-PASS RTCSctpTransport interface: existence and properties of interface prototype object
-PASS RTCSctpTransport interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCSctpTransport interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCSctpTransport interface: attribute transport
-PASS RTCSctpTransport interface: attribute state
-PASS RTCSctpTransport interface: attribute maxMessageSize
-PASS RTCSctpTransport interface: attribute maxChannels
-PASS RTCSctpTransport interface: attribute onstatechange
-FAIL RTCSctpTransport must be primary interface of idlTestObjects.sctpTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL Stringification of idlTestObjects.sctpTransport assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "transport" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "state" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "maxMessageSize" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "maxChannels" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCSctpTransport interface: idlTestObjects.sctpTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-PASS RTCDataChannel interface: existence and properties of interface object
-PASS RTCDataChannel interface object length
-PASS RTCDataChannel interface object name
-PASS RTCDataChannel interface: existence and properties of interface prototype object
-PASS RTCDataChannel interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCDataChannel interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCDataChannel interface: attribute label
-PASS RTCDataChannel interface: attribute ordered
-PASS RTCDataChannel interface: attribute maxPacketLifeTime
-PASS RTCDataChannel interface: attribute maxRetransmits
-PASS RTCDataChannel interface: attribute protocol
-PASS RTCDataChannel interface: attribute negotiated
-PASS RTCDataChannel interface: attribute id
-PASS RTCDataChannel interface: attribute readyState
-PASS RTCDataChannel interface: attribute bufferedAmount
-PASS RTCDataChannel interface: attribute bufferedAmountLowThreshold
-PASS RTCDataChannel interface: attribute onopen
-PASS RTCDataChannel interface: attribute onbufferedamountlow
-PASS RTCDataChannel interface: attribute onerror
-PASS RTCDataChannel interface: attribute onclosing
-PASS RTCDataChannel interface: attribute onclose
-PASS RTCDataChannel interface: operation close()
-PASS RTCDataChannel interface: attribute onmessage
-PASS RTCDataChannel interface: attribute binaryType
-PASS RTCDataChannel interface: operation send(USVString)
-PASS RTCDataChannel interface: operation send(Blob)
-PASS RTCDataChannel interface: operation send(ArrayBuffer)
-PASS RTCDataChannel interface: operation send(ArrayBufferView)
-PASS RTCDataChannel must be primary interface of new RTCPeerConnection().createDataChannel('')
-PASS Stringification of new RTCPeerConnection().createDataChannel('')
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "label" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "ordered" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "maxPacketLifeTime" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "maxRetransmits" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "protocol" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "negotiated" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "id" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "readyState" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "bufferedAmount" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "bufferedAmountLowThreshold" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onopen" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onbufferedamountlow" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onerror" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onclosing" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onclose" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "close()" with the proper type
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onmessage" with the proper type
-FAIL RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "binaryType" with the proper type Right-hand side of 'instanceof' is not an object
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "send(USVString)" with the proper type
-PASS RTCDataChannel interface: calling send(USVString) on new RTCPeerConnection().createDataChannel('') with too few arguments must throw TypeError
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "send(Blob)" with the proper type
-PASS RTCDataChannel interface: calling send(Blob) on new RTCPeerConnection().createDataChannel('') with too few arguments must throw TypeError
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "send(ArrayBuffer)" with the proper type
-PASS RTCDataChannel interface: calling send(ArrayBuffer) on new RTCPeerConnection().createDataChannel('') with too few arguments must throw TypeError
-PASS RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "send(ArrayBufferView)" with the proper type
-PASS RTCDataChannel interface: calling send(ArrayBufferView) on new RTCPeerConnection().createDataChannel('') with too few arguments must throw TypeError
-PASS RTCDataChannelEvent interface: existence and properties of interface object
-PASS RTCDataChannelEvent interface object length
-PASS RTCDataChannelEvent interface object name
-PASS RTCDataChannelEvent interface: existence and properties of interface prototype object
-PASS RTCDataChannelEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCDataChannelEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCDataChannelEvent interface: attribute channel
-PASS RTCDataChannelEvent must be primary interface of new RTCDataChannelEvent('channel', {
-          channel: new RTCPeerConnection().createDataChannel('')
-        })
-PASS Stringification of new RTCDataChannelEvent('channel', {
-          channel: new RTCPeerConnection().createDataChannel('')
-        })
-PASS RTCDataChannelEvent interface: new RTCDataChannelEvent('channel', {
-          channel: new RTCPeerConnection().createDataChannel('')
-        }) must inherit property "channel" with the proper type
-PASS RTCDTMFSender interface: existence and properties of interface object
-PASS RTCDTMFSender interface object length
-PASS RTCDTMFSender interface object name
-PASS RTCDTMFSender interface: existence and properties of interface prototype object
-PASS RTCDTMFSender interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCDTMFSender interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCDTMFSender interface: operation insertDTMF(DOMString, optional unsigned long, optional unsigned long)
-PASS RTCDTMFSender interface: attribute ontonechange
-PASS RTCDTMFSender interface: attribute canInsertDTMF
-PASS RTCDTMFSender interface: attribute toneBuffer
-PASS RTCDTMFToneChangeEvent interface: existence and properties of interface object
-FAIL RTCDTMFToneChangeEvent interface object length assert_equals: wrong value for RTCDTMFToneChangeEvent.length expected 1 but got 2
-PASS RTCDTMFToneChangeEvent interface object name
-PASS RTCDTMFToneChangeEvent interface: existence and properties of interface prototype object
-PASS RTCDTMFToneChangeEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCDTMFToneChangeEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCDTMFToneChangeEvent interface: attribute tone
-PASS RTCStatsReport interface: existence and properties of interface object
-PASS RTCStatsReport interface object length
-PASS RTCStatsReport interface object name
-PASS RTCStatsReport interface: existence and properties of interface prototype object
-PASS RTCStatsReport interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCStatsReport interface: existence and properties of interface prototype object's @@unscopables property
-FAIL RTCStatsReport interface: maplike<DOMString, object> assert_equals: entries enumerable expected false but got true
-PASS RTCError interface: existence and properties of interface object
-PASS RTCError interface object length
-PASS RTCError interface object name
-PASS RTCError interface: existence and properties of interface prototype object
-PASS RTCError interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCError interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCError interface: attribute errorDetail
-PASS RTCError interface: attribute sdpLineNumber
-PASS RTCError interface: attribute sctpCauseCode
-PASS RTCError interface: attribute receivedAlert
-PASS RTCError interface: attribute sentAlert
-PASS RTCErrorEvent interface: existence and properties of interface object
-PASS RTCErrorEvent interface object length
-PASS RTCErrorEvent interface object name
-PASS RTCErrorEvent interface: existence and properties of interface prototype object
-PASS RTCErrorEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS RTCErrorEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS RTCErrorEvent interface: attribute error
-FAIL RTCErrorEvent must be primary interface of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
-FAIL Stringification of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
-FAIL RTCErrorEvent interface: new RTCErrorEvent('error') must inherit property "error" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
deleted file mode 100644
index 018b4ba..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-FAIL createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers assert_equals: Expect audio line to remain in created offer expected 1 but got 0
-FAIL createOffer() with offerToReceiveVideo should add video line to all subsequent created offers assert_equals: Expect video line to remain in created offer expected 1 but got 0
-FAIL createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line assert_equals: Expect audio line to remain in created offer expected 1 but got 0
-PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
-FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
-PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
-FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'.
-FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
deleted file mode 100644
index 46fc15d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL checkAddTransceiverWithStream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL checkAddTransceiverWithOfferToReceiveAudio assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
-FAIL checkAddTransceiverWithOfferToReceiveVideo assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
-FAIL checkAddTransceiverWithOfferToReceiveBoth assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false},{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/onaddstream.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
deleted file mode 100644
index 7415aac..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught Error: assert_true: Transceiver is set on event expected true got false
-FAIL Check onaddstream assert_equals: expected "[{streams:[{}]},{streams:[{}]}]" but got "[]"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/no-media-call-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/no-media-call-expected.txt
deleted file mode 100644
index 3961ea0..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/no-media-call-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Can set up a basic WebRTC call with no data. assert_equals: expected (number) 1 but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/RTCPeerConnection-payloadTypes-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/RTCPeerConnection-payloadTypes-expected.txt
deleted file mode 100644
index fc84ead..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/RTCPeerConnection-payloadTypes-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL createOffer with the maximum set of codecs does not generate invalid payload types promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/bundle.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/bundle.https-expected.txt
deleted file mode 100644
index f74e59d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/bundle.https-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL not negotiating BUNDLE creates two separate ice and dtls transports assert_not_equals: got disallowed value null
-FAIL bundles on the first transport and closes the second assert_not_equals: got disallowed value null
-FAIL max-bundle with an offer without bundle only negotiates the first m-line promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: max-bundle is used but no bundle group found."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/candidate-exchange.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/candidate-exchange.https-expected.txt
deleted file mode 100644
index 12dac25..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/candidate-exchange.https-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS Two way ICE exchange works
-FAIL Adding only caller -> callee candidates gives a connection promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL Adding only callee -> caller candidates gives a connection promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL Adding callee -> caller candidates from end-of-candidates gives a connection promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL Explicit offer/answer exchange gives a connection promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-PASS Candidates always arrive after setLocalDescription(offer) resolves
-PASS Candidates always arrive after setLocalDescription(answer) resolves
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/crypto-suite.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/crypto-suite.https-expected.txt
deleted file mode 100644
index e286cfae..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/crypto-suite.https-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-FAIL tlsVersion is acceptable on data-only promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL tlsVersion is acceptable on video-only promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL dtlsCipher is acceptable on data-only promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL dtlsCipher is acceptable on video-only promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL srtpCipher is acceptable on data-only promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL srtpCipher is acceptable on video-only promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL tlsGroup is acceptable on data-only promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-FAIL tlsGroup is acceptable on video-only promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
deleted file mode 100644
index 8c718e5f..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This is a testharness.js-based test.
-FAIL Level 1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 3 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 4 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 5 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 6 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 1.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 1.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 1.3 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 2.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 2.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 3.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 3.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 4.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 4.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 5.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 5.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 6.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Level 6.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/handover-datachannel-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/handover-datachannel-expected.txt
deleted file mode 100644
index 8eb1122..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/handover-datachannel-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Handover with datachannel reinitiated from new callee completes promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'transport')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/handover-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/handover-expected.txt
deleted file mode 100644
index bf69c17..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/handover-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negotiation of handover initiated at caller works promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL Negotiation of handover initiated at callee works promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/ice-state.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/ice-state.https-expected.txt
deleted file mode 100644
index f358b55..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/ice-state.https-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS PC should enter connected (or completed) state when candidates are sent
-PASS PC should generate offer with a=ice-options:trickle
-FAIL PC should enter disconnected state when a failing candidate is sent promise_test: Unhandled rejection with value: "Unexpected state encountered: failed"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/msid-parse-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/msid-parse-expected.txt
deleted file mode 100644
index e6f3f37..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/msid-parse-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS Description with no msid produces a track with a stream
-FAIL Description with msid:- appid produces a track with no stream assert_equals: expected 0 but got 1
-FAIL Description with msid:foo bar produces a stream with id foo assert_equals: expected "foo" but got "default"
-FAIL Description with two msid produces two streams assert_equals: expected 2 but got 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-extension-support-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-extension-support-expected.txt
deleted file mode 100644
index ab49cd27..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/rtp-extension-support-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL RTP header extension urn:ietf:params:rtp-hdrext:ssrc-audio-level is present in offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTP header extension urn:ietf:params:rtp-hdrext:sdes:mid is present in offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTP header extension urn:3gpp:video-orientation is present in offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTP header extension urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id is present in offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTP header extension urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id is present in offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL RTP header extension reassignment causes failure promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/sdes-dont-dont-dont-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/sdes-dont-dont-dont-expected.txt
deleted file mode 100644
index 27e1dae..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/sdes-dont-dont-dont-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL does not create offers with SDES promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-PASS rejects a remote offer that only includes SDES and no DTLS fingerprint
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/simulcast-answer-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/simulcast-answer-expected.txt
deleted file mode 100644
index 0c3da53..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/simulcast-answer-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL createAnswer() with multiple send encodings should create simulcast answer promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of undefined (setting 'direction')"
-FAIL Using the ~rid SDP syntax in a remote offer does not control the local encodings active flag promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of undefined (setting 'direction')"
-FAIL Disabling encodings locally does not change the SDP promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of undefined (setting 'direction')"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/simulcast-offer-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/simulcast-offer-expected.txt
deleted file mode 100644
index 7c4b62f0..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/simulcast-offer-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL createOffer() with multiple send encodings should create simulcast offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/receiver-track-live.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/receiver-track-live.https-expected.txt
deleted file mode 100644
index a1a6e49..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/receiver-track-live.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS Setup audio call
-FAIL Inactivate the audio transceiver promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of undefined (setting 'direction')"
-FAIL Reactivate the audio transceiver promise_test: Unhandled rejection with value: object "TypeError: Cannot set properties of undefined (setting 'direction')"
-PASS Clean-up
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/recvonly-transceiver-can-become-sendrecv.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/recvonly-transceiver-can-become-sendrecv.https-expected.txt
deleted file mode 100644
index 926dc48c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/recvonly-transceiver-can-become-sendrecv.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL [audio] recvonly transceiver can become sendrecv promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-FAIL [video] recvonly transceiver can become sendrecv promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/basic.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/basic.https-expected.txt
deleted file mode 100644
index abf7c3fe..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/basic.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Basic simulcast setup with two spatial layers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/getStats.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/getStats.https-expected.txt
deleted file mode 100644
index 48dafa2c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/getStats.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Simulcast getStats results promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/h264.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/h264.https-expected.txt
deleted file mode 100644
index b2d7a3d..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/h264.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL H264 simulcast setup with two spatial layers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/setParameters-active.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/setParameters-active.https-expected.txt
deleted file mode 100644
index 0282bcb..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/setParameters-active.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Simulcast setParameters active=false stops sending frames promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/vp8.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/vp8.https-expected.txt
deleted file mode 100644
index 8434d82c..0000000
--- a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/simulcast/vp8.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL VP8 simulcast setup with two spatial layers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
new file mode 100644
index 0000000..3ddcf6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
@@ -0,0 +1,186 @@
+This is a testharness.js-based test.
+Found 182 tests; 179 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Value Properties of the Global Object
+PASS Value Property: NaN
+PASS Value Property: Infinity
+PASS Value Property: undefined
+PASS Function Properties of the Global Object
+PASS Function Property: eval
+PASS Function Property: parseInt
+PASS Function Property: parseFloat
+PASS Function Property: isNaN
+PASS Function Property: isFinite
+PASS URI Handling Function Properties
+PASS URI Handling Function Property: decodeURI
+PASS URI Handling Function Property: decodeURIComponent
+PASS URI Handling Function Property: encodeURI
+PASS URI Handling Function Property: encodeURIComponent
+PASS Constructor Properties of the Global Object
+PASS Constructor Property: Object
+PASS Constructor Property: Function
+PASS Constructor Property: Array
+PASS Constructor Property: String
+PASS Constructor Property: Boolean
+PASS Constructor Property: Number
+PASS Constructor Property: Date
+PASS Constructor Property: RegExp
+PASS Constructor Property: Error
+PASS Constructor Property: EvalError
+PASS Constructor Property: RangeError
+PASS Constructor Property: ReferenceError
+PASS Constructor Property: SyntaxError
+PASS Constructor Property: TypeError
+PASS Constructor Property: URIError
+PASS Other Properties of the Global Object
+PASS Other Property: Math
+PASS Other Property: JSON
+PASS EventTarget interface
+PASS EventTarget method: addEventListener
+PASS EventTarget method: removeEventListener
+PASS EventTarget method: dispatchEvent
+PASS Window interface
+PASS Window method: close
+PASS Window method: stop
+PASS Window method: focus
+PASS Window method: blur
+PASS Window method: open
+PASS Window method: alert
+PASS Window method: confirm
+PASS Window method: prompt
+PASS Window method: print
+PASS Window method: postMessage
+PASS Window method: btoa
+PASS Window method: atob
+PASS Window method: setTimeout
+PASS Window method: clearTimeout
+PASS Window method: setInterval
+PASS Window method: clearInterval
+PASS Window method: queueMicrotask
+PASS Window method: createImageBitmap
+PASS Window method: getSelection
+PASS Window method: getComputedStyle
+PASS Window method: matchMedia
+PASS Window method: moveBy
+PASS Window method: moveTo
+PASS Window method: resizeBy
+PASS Window method: resizeTo
+PASS Window method: scroll
+PASS Window method: scrollTo
+PASS Window method: scrollBy
+PASS Window readonly attribute: history
+PASS Window readonly attribute: frameElement
+PASS Window readonly attribute: navigator
+PASS Window readonly attribute: sessionStorage
+PASS Window readonly attribute: localStorage
+PASS Window attribute: name
+PASS Window attribute: status
+PASS Window attribute: opener
+PASS Window attribute: onabort
+PASS Window attribute: onafterprint
+PASS Window attribute: onbeforeprint
+PASS Window attribute: onbeforeunload
+PASS Window attribute: onblur
+PASS Window attribute: oncancel
+PASS Window attribute: oncanplay
+PASS Window attribute: oncanplaythrough
+PASS Window attribute: onchange
+PASS Window attribute: onclick
+PASS Window attribute: onclose
+PASS Window attribute: oncontextmenu
+PASS Window attribute: oncuechange
+PASS Window attribute: ondblclick
+PASS Window attribute: ondrag
+PASS Window attribute: ondragend
+PASS Window attribute: ondragenter
+PASS Window attribute: ondragleave
+PASS Window attribute: ondragover
+PASS Window attribute: ondragstart
+PASS Window attribute: ondrop
+PASS Window attribute: ondurationchange
+PASS Window attribute: onemptied
+PASS Window attribute: onended
+PASS Window attribute: onerror
+PASS Window attribute: onfocus
+PASS Window attribute: onhashchange
+PASS Window attribute: oninput
+PASS Window attribute: oninvalid
+PASS Window attribute: onkeydown
+PASS Window attribute: onkeypress
+PASS Window attribute: onkeyup
+PASS Window attribute: onload
+PASS Window attribute: onloadeddata
+PASS Window attribute: onloadedmetadata
+PASS Window attribute: onloadstart
+PASS Window attribute: onmessage
+PASS Window attribute: onmousedown
+PASS Window attribute: onmousemove
+PASS Window attribute: onmouseout
+PASS Window attribute: onmouseover
+PASS Window attribute: onmouseup
+PASS Window attribute: onmousewheel
+PASS Window attribute: onoffline
+PASS Window attribute: ononline
+PASS Window attribute: onpause
+PASS Window attribute: onplay
+PASS Window attribute: onplaying
+PASS Window attribute: onpagehide
+PASS Window attribute: onpageshow
+PASS Window attribute: onpopstate
+PASS Window attribute: onprogress
+PASS Window attribute: onratechange
+PASS Window attribute: onreset
+PASS Window attribute: onresize
+PASS Window attribute: onscroll
+PASS Window attribute: onseeked
+PASS Window attribute: onseeking
+PASS Window attribute: onselect
+PASS Window attribute: onstalled
+PASS Window attribute: onstorage
+PASS Window attribute: onsubmit
+PASS Window attribute: onsuspend
+PASS Window attribute: ontimeupdate
+PASS Window attribute: onunload
+PASS Window attribute: onvolumechange
+PASS Window attribute: onwaiting
+PASS Window unforgeable attribute: window
+PASS Window unforgeable attribute: document
+PASS Window unforgeable attribute: location
+PASS Window unforgeable attribute: top
+PASS Window replaceable attribute: self
+PASS Window replaceable attribute: locationbar
+PASS Window replaceable attribute: menubar
+PASS Window replaceable attribute: personalbar
+PASS Window replaceable attribute: scrollbars
+PASS Window replaceable attribute: statusbar
+PASS Window replaceable attribute: toolbar
+PASS Window replaceable attribute: frames
+PASS Window replaceable attribute: parent
+PASS Window replaceable attribute: external
+PASS Window replaceable attribute: length
+PASS Window replaceable attribute: origin
+PASS Window replaceable attribute: screen
+PASS Window replaceable attribute: scrollX
+PASS Window replaceable attribute: scrollY
+PASS Window replaceable attribute: pageXOffset
+PASS Window replaceable attribute: pageYOffset
+PASS Window replaceable attribute: innerWidth
+PASS Window replaceable attribute: innerHeight
+PASS Window replaceable attribute: screenLeft
+PASS Window replaceable attribute: screenTop
+PASS Window replaceable attribute: screenX
+PASS Window replaceable attribute: screenY
+PASS Window replaceable attribute: outerWidth
+PASS Window replaceable attribute: outerHeight
+PASS Window replaceable attribute: devicePixelRatio
+PASS constructor
+PASS Window readonly getter: window
+PASS Window readonly getter in iframe: window
+FAIL Window readonly getter in detached iframe: window assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: self
+PASS Window readonly getter in iframe: self
+FAIL Window readonly getter in detached iframe: self assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: frames
+PASS Window readonly getter in iframe: frames
+FAIL Window readonly getter in detached iframe: frames assert_equals: expected object "[object Window]" but got null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
new file mode 100644
index 0000000..3ddcf6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
@@ -0,0 +1,186 @@
+This is a testharness.js-based test.
+Found 182 tests; 179 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Value Properties of the Global Object
+PASS Value Property: NaN
+PASS Value Property: Infinity
+PASS Value Property: undefined
+PASS Function Properties of the Global Object
+PASS Function Property: eval
+PASS Function Property: parseInt
+PASS Function Property: parseFloat
+PASS Function Property: isNaN
+PASS Function Property: isFinite
+PASS URI Handling Function Properties
+PASS URI Handling Function Property: decodeURI
+PASS URI Handling Function Property: decodeURIComponent
+PASS URI Handling Function Property: encodeURI
+PASS URI Handling Function Property: encodeURIComponent
+PASS Constructor Properties of the Global Object
+PASS Constructor Property: Object
+PASS Constructor Property: Function
+PASS Constructor Property: Array
+PASS Constructor Property: String
+PASS Constructor Property: Boolean
+PASS Constructor Property: Number
+PASS Constructor Property: Date
+PASS Constructor Property: RegExp
+PASS Constructor Property: Error
+PASS Constructor Property: EvalError
+PASS Constructor Property: RangeError
+PASS Constructor Property: ReferenceError
+PASS Constructor Property: SyntaxError
+PASS Constructor Property: TypeError
+PASS Constructor Property: URIError
+PASS Other Properties of the Global Object
+PASS Other Property: Math
+PASS Other Property: JSON
+PASS EventTarget interface
+PASS EventTarget method: addEventListener
+PASS EventTarget method: removeEventListener
+PASS EventTarget method: dispatchEvent
+PASS Window interface
+PASS Window method: close
+PASS Window method: stop
+PASS Window method: focus
+PASS Window method: blur
+PASS Window method: open
+PASS Window method: alert
+PASS Window method: confirm
+PASS Window method: prompt
+PASS Window method: print
+PASS Window method: postMessage
+PASS Window method: btoa
+PASS Window method: atob
+PASS Window method: setTimeout
+PASS Window method: clearTimeout
+PASS Window method: setInterval
+PASS Window method: clearInterval
+PASS Window method: queueMicrotask
+PASS Window method: createImageBitmap
+PASS Window method: getSelection
+PASS Window method: getComputedStyle
+PASS Window method: matchMedia
+PASS Window method: moveBy
+PASS Window method: moveTo
+PASS Window method: resizeBy
+PASS Window method: resizeTo
+PASS Window method: scroll
+PASS Window method: scrollTo
+PASS Window method: scrollBy
+PASS Window readonly attribute: history
+PASS Window readonly attribute: frameElement
+PASS Window readonly attribute: navigator
+PASS Window readonly attribute: sessionStorage
+PASS Window readonly attribute: localStorage
+PASS Window attribute: name
+PASS Window attribute: status
+PASS Window attribute: opener
+PASS Window attribute: onabort
+PASS Window attribute: onafterprint
+PASS Window attribute: onbeforeprint
+PASS Window attribute: onbeforeunload
+PASS Window attribute: onblur
+PASS Window attribute: oncancel
+PASS Window attribute: oncanplay
+PASS Window attribute: oncanplaythrough
+PASS Window attribute: onchange
+PASS Window attribute: onclick
+PASS Window attribute: onclose
+PASS Window attribute: oncontextmenu
+PASS Window attribute: oncuechange
+PASS Window attribute: ondblclick
+PASS Window attribute: ondrag
+PASS Window attribute: ondragend
+PASS Window attribute: ondragenter
+PASS Window attribute: ondragleave
+PASS Window attribute: ondragover
+PASS Window attribute: ondragstart
+PASS Window attribute: ondrop
+PASS Window attribute: ondurationchange
+PASS Window attribute: onemptied
+PASS Window attribute: onended
+PASS Window attribute: onerror
+PASS Window attribute: onfocus
+PASS Window attribute: onhashchange
+PASS Window attribute: oninput
+PASS Window attribute: oninvalid
+PASS Window attribute: onkeydown
+PASS Window attribute: onkeypress
+PASS Window attribute: onkeyup
+PASS Window attribute: onload
+PASS Window attribute: onloadeddata
+PASS Window attribute: onloadedmetadata
+PASS Window attribute: onloadstart
+PASS Window attribute: onmessage
+PASS Window attribute: onmousedown
+PASS Window attribute: onmousemove
+PASS Window attribute: onmouseout
+PASS Window attribute: onmouseover
+PASS Window attribute: onmouseup
+PASS Window attribute: onmousewheel
+PASS Window attribute: onoffline
+PASS Window attribute: ononline
+PASS Window attribute: onpause
+PASS Window attribute: onplay
+PASS Window attribute: onplaying
+PASS Window attribute: onpagehide
+PASS Window attribute: onpageshow
+PASS Window attribute: onpopstate
+PASS Window attribute: onprogress
+PASS Window attribute: onratechange
+PASS Window attribute: onreset
+PASS Window attribute: onresize
+PASS Window attribute: onscroll
+PASS Window attribute: onseeked
+PASS Window attribute: onseeking
+PASS Window attribute: onselect
+PASS Window attribute: onstalled
+PASS Window attribute: onstorage
+PASS Window attribute: onsubmit
+PASS Window attribute: onsuspend
+PASS Window attribute: ontimeupdate
+PASS Window attribute: onunload
+PASS Window attribute: onvolumechange
+PASS Window attribute: onwaiting
+PASS Window unforgeable attribute: window
+PASS Window unforgeable attribute: document
+PASS Window unforgeable attribute: location
+PASS Window unforgeable attribute: top
+PASS Window replaceable attribute: self
+PASS Window replaceable attribute: locationbar
+PASS Window replaceable attribute: menubar
+PASS Window replaceable attribute: personalbar
+PASS Window replaceable attribute: scrollbars
+PASS Window replaceable attribute: statusbar
+PASS Window replaceable attribute: toolbar
+PASS Window replaceable attribute: frames
+PASS Window replaceable attribute: parent
+PASS Window replaceable attribute: external
+PASS Window replaceable attribute: length
+PASS Window replaceable attribute: origin
+PASS Window replaceable attribute: screen
+PASS Window replaceable attribute: scrollX
+PASS Window replaceable attribute: scrollY
+PASS Window replaceable attribute: pageXOffset
+PASS Window replaceable attribute: pageYOffset
+PASS Window replaceable attribute: innerWidth
+PASS Window replaceable attribute: innerHeight
+PASS Window replaceable attribute: screenLeft
+PASS Window replaceable attribute: screenTop
+PASS Window replaceable attribute: screenX
+PASS Window replaceable attribute: screenY
+PASS Window replaceable attribute: outerWidth
+PASS Window replaceable attribute: outerHeight
+PASS Window replaceable attribute: devicePixelRatio
+PASS constructor
+PASS Window readonly getter: window
+PASS Window readonly getter in iframe: window
+FAIL Window readonly getter in detached iframe: window assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: self
+PASS Window readonly getter in iframe: self
+FAIL Window readonly getter in detached iframe: self assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: frames
+PASS Window readonly getter in iframe: frames
+FAIL Window readonly getter in detached iframe: frames assert_equals: expected object "[object Window]" but got null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
new file mode 100644
index 0000000..3ddcf6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
@@ -0,0 +1,186 @@
+This is a testharness.js-based test.
+Found 182 tests; 179 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Value Properties of the Global Object
+PASS Value Property: NaN
+PASS Value Property: Infinity
+PASS Value Property: undefined
+PASS Function Properties of the Global Object
+PASS Function Property: eval
+PASS Function Property: parseInt
+PASS Function Property: parseFloat
+PASS Function Property: isNaN
+PASS Function Property: isFinite
+PASS URI Handling Function Properties
+PASS URI Handling Function Property: decodeURI
+PASS URI Handling Function Property: decodeURIComponent
+PASS URI Handling Function Property: encodeURI
+PASS URI Handling Function Property: encodeURIComponent
+PASS Constructor Properties of the Global Object
+PASS Constructor Property: Object
+PASS Constructor Property: Function
+PASS Constructor Property: Array
+PASS Constructor Property: String
+PASS Constructor Property: Boolean
+PASS Constructor Property: Number
+PASS Constructor Property: Date
+PASS Constructor Property: RegExp
+PASS Constructor Property: Error
+PASS Constructor Property: EvalError
+PASS Constructor Property: RangeError
+PASS Constructor Property: ReferenceError
+PASS Constructor Property: SyntaxError
+PASS Constructor Property: TypeError
+PASS Constructor Property: URIError
+PASS Other Properties of the Global Object
+PASS Other Property: Math
+PASS Other Property: JSON
+PASS EventTarget interface
+PASS EventTarget method: addEventListener
+PASS EventTarget method: removeEventListener
+PASS EventTarget method: dispatchEvent
+PASS Window interface
+PASS Window method: close
+PASS Window method: stop
+PASS Window method: focus
+PASS Window method: blur
+PASS Window method: open
+PASS Window method: alert
+PASS Window method: confirm
+PASS Window method: prompt
+PASS Window method: print
+PASS Window method: postMessage
+PASS Window method: btoa
+PASS Window method: atob
+PASS Window method: setTimeout
+PASS Window method: clearTimeout
+PASS Window method: setInterval
+PASS Window method: clearInterval
+PASS Window method: queueMicrotask
+PASS Window method: createImageBitmap
+PASS Window method: getSelection
+PASS Window method: getComputedStyle
+PASS Window method: matchMedia
+PASS Window method: moveBy
+PASS Window method: moveTo
+PASS Window method: resizeBy
+PASS Window method: resizeTo
+PASS Window method: scroll
+PASS Window method: scrollTo
+PASS Window method: scrollBy
+PASS Window readonly attribute: history
+PASS Window readonly attribute: frameElement
+PASS Window readonly attribute: navigator
+PASS Window readonly attribute: sessionStorage
+PASS Window readonly attribute: localStorage
+PASS Window attribute: name
+PASS Window attribute: status
+PASS Window attribute: opener
+PASS Window attribute: onabort
+PASS Window attribute: onafterprint
+PASS Window attribute: onbeforeprint
+PASS Window attribute: onbeforeunload
+PASS Window attribute: onblur
+PASS Window attribute: oncancel
+PASS Window attribute: oncanplay
+PASS Window attribute: oncanplaythrough
+PASS Window attribute: onchange
+PASS Window attribute: onclick
+PASS Window attribute: onclose
+PASS Window attribute: oncontextmenu
+PASS Window attribute: oncuechange
+PASS Window attribute: ondblclick
+PASS Window attribute: ondrag
+PASS Window attribute: ondragend
+PASS Window attribute: ondragenter
+PASS Window attribute: ondragleave
+PASS Window attribute: ondragover
+PASS Window attribute: ondragstart
+PASS Window attribute: ondrop
+PASS Window attribute: ondurationchange
+PASS Window attribute: onemptied
+PASS Window attribute: onended
+PASS Window attribute: onerror
+PASS Window attribute: onfocus
+PASS Window attribute: onhashchange
+PASS Window attribute: oninput
+PASS Window attribute: oninvalid
+PASS Window attribute: onkeydown
+PASS Window attribute: onkeypress
+PASS Window attribute: onkeyup
+PASS Window attribute: onload
+PASS Window attribute: onloadeddata
+PASS Window attribute: onloadedmetadata
+PASS Window attribute: onloadstart
+PASS Window attribute: onmessage
+PASS Window attribute: onmousedown
+PASS Window attribute: onmousemove
+PASS Window attribute: onmouseout
+PASS Window attribute: onmouseover
+PASS Window attribute: onmouseup
+PASS Window attribute: onmousewheel
+PASS Window attribute: onoffline
+PASS Window attribute: ononline
+PASS Window attribute: onpause
+PASS Window attribute: onplay
+PASS Window attribute: onplaying
+PASS Window attribute: onpagehide
+PASS Window attribute: onpageshow
+PASS Window attribute: onpopstate
+PASS Window attribute: onprogress
+PASS Window attribute: onratechange
+PASS Window attribute: onreset
+PASS Window attribute: onresize
+PASS Window attribute: onscroll
+PASS Window attribute: onseeked
+PASS Window attribute: onseeking
+PASS Window attribute: onselect
+PASS Window attribute: onstalled
+PASS Window attribute: onstorage
+PASS Window attribute: onsubmit
+PASS Window attribute: onsuspend
+PASS Window attribute: ontimeupdate
+PASS Window attribute: onunload
+PASS Window attribute: onvolumechange
+PASS Window attribute: onwaiting
+PASS Window unforgeable attribute: window
+PASS Window unforgeable attribute: document
+PASS Window unforgeable attribute: location
+PASS Window unforgeable attribute: top
+PASS Window replaceable attribute: self
+PASS Window replaceable attribute: locationbar
+PASS Window replaceable attribute: menubar
+PASS Window replaceable attribute: personalbar
+PASS Window replaceable attribute: scrollbars
+PASS Window replaceable attribute: statusbar
+PASS Window replaceable attribute: toolbar
+PASS Window replaceable attribute: frames
+PASS Window replaceable attribute: parent
+PASS Window replaceable attribute: external
+PASS Window replaceable attribute: length
+PASS Window replaceable attribute: origin
+PASS Window replaceable attribute: screen
+PASS Window replaceable attribute: scrollX
+PASS Window replaceable attribute: scrollY
+PASS Window replaceable attribute: pageXOffset
+PASS Window replaceable attribute: pageYOffset
+PASS Window replaceable attribute: innerWidth
+PASS Window replaceable attribute: innerHeight
+PASS Window replaceable attribute: screenLeft
+PASS Window replaceable attribute: screenTop
+PASS Window replaceable attribute: screenX
+PASS Window replaceable attribute: screenY
+PASS Window replaceable attribute: outerWidth
+PASS Window replaceable attribute: outerHeight
+PASS Window replaceable attribute: devicePixelRatio
+PASS constructor
+PASS Window readonly getter: window
+PASS Window readonly getter in iframe: window
+FAIL Window readonly getter in detached iframe: window assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: self
+PASS Window readonly getter in iframe: self
+FAIL Window readonly getter in detached iframe: self assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: frames
+PASS Window readonly getter in iframe: frames
+FAIL Window readonly getter in detached iframe: frames assert_equals: expected object "[object Window]" but got null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
new file mode 100644
index 0000000..50d4f15e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
@@ -0,0 +1,186 @@
+This is a testharness.js-based test.
+Found 182 tests; 179 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Value Properties of the Global Object
+PASS Value Property: NaN
+PASS Value Property: Infinity
+PASS Value Property: undefined
+PASS Function Properties of the Global Object
+PASS Function Property: eval
+PASS Function Property: parseInt
+PASS Function Property: parseFloat
+PASS Function Property: isNaN
+PASS Function Property: isFinite
+PASS URI Handling Function Properties
+PASS URI Handling Function Property: decodeURI
+PASS URI Handling Function Property: decodeURIComponent
+PASS URI Handling Function Property: encodeURI
+PASS URI Handling Function Property: encodeURIComponent
+PASS Constructor Properties of the Global Object
+PASS Constructor Property: Object
+PASS Constructor Property: Function
+PASS Constructor Property: Array
+PASS Constructor Property: String
+PASS Constructor Property: Boolean
+PASS Constructor Property: Number
+PASS Constructor Property: Date
+PASS Constructor Property: RegExp
+PASS Constructor Property: Error
+PASS Constructor Property: EvalError
+PASS Constructor Property: RangeError
+PASS Constructor Property: ReferenceError
+PASS Constructor Property: SyntaxError
+PASS Constructor Property: TypeError
+PASS Constructor Property: URIError
+PASS Other Properties of the Global Object
+PASS Other Property: Math
+PASS Other Property: JSON
+PASS EventTarget interface
+PASS EventTarget method: addEventListener
+PASS EventTarget method: removeEventListener
+PASS EventTarget method: dispatchEvent
+PASS Window interface
+PASS Window method: close
+PASS Window method: stop
+PASS Window method: focus
+PASS Window method: blur
+PASS Window method: open
+PASS Window method: alert
+PASS Window method: confirm
+PASS Window method: prompt
+PASS Window method: print
+PASS Window method: postMessage
+PASS Window method: btoa
+PASS Window method: atob
+PASS Window method: setTimeout
+PASS Window method: clearTimeout
+PASS Window method: setInterval
+PASS Window method: clearInterval
+PASS Window method: queueMicrotask
+PASS Window method: createImageBitmap
+PASS Window method: getSelection
+PASS Window method: getComputedStyle
+PASS Window method: matchMedia
+PASS Window method: moveBy
+PASS Window method: moveTo
+PASS Window method: resizeBy
+PASS Window method: resizeTo
+PASS Window method: scroll
+PASS Window method: scrollTo
+PASS Window method: scrollBy
+PASS Window readonly attribute: history
+PASS Window readonly attribute: frameElement
+PASS Window readonly attribute: navigator
+PASS Window readonly attribute: sessionStorage
+PASS Window readonly attribute: localStorage
+PASS Window attribute: name
+PASS Window attribute: status
+PASS Window attribute: opener
+PASS Window attribute: onabort
+PASS Window attribute: onafterprint
+PASS Window attribute: onbeforeprint
+PASS Window attribute: onbeforeunload
+PASS Window attribute: onblur
+PASS Window attribute: oncancel
+PASS Window attribute: oncanplay
+PASS Window attribute: oncanplaythrough
+PASS Window attribute: onchange
+PASS Window attribute: onclick
+PASS Window attribute: onclose
+PASS Window attribute: oncontextmenu
+PASS Window attribute: oncuechange
+PASS Window attribute: ondblclick
+PASS Window attribute: ondrag
+PASS Window attribute: ondragend
+PASS Window attribute: ondragenter
+PASS Window attribute: ondragleave
+PASS Window attribute: ondragover
+PASS Window attribute: ondragstart
+PASS Window attribute: ondrop
+PASS Window attribute: ondurationchange
+PASS Window attribute: onemptied
+PASS Window attribute: onended
+PASS Window attribute: onerror
+PASS Window attribute: onfocus
+PASS Window attribute: onhashchange
+PASS Window attribute: oninput
+PASS Window attribute: oninvalid
+PASS Window attribute: onkeydown
+PASS Window attribute: onkeypress
+PASS Window attribute: onkeyup
+PASS Window attribute: onload
+PASS Window attribute: onloadeddata
+PASS Window attribute: onloadedmetadata
+PASS Window attribute: onloadstart
+PASS Window attribute: onmessage
+PASS Window attribute: onmousedown
+PASS Window attribute: onmousemove
+PASS Window attribute: onmouseout
+PASS Window attribute: onmouseover
+PASS Window attribute: onmouseup
+PASS Window attribute: onmousewheel
+PASS Window attribute: onoffline
+PASS Window attribute: ononline
+PASS Window attribute: onpause
+PASS Window attribute: onplay
+PASS Window attribute: onplaying
+PASS Window attribute: onpagehide
+PASS Window attribute: onpageshow
+PASS Window attribute: onpopstate
+PASS Window attribute: onprogress
+PASS Window attribute: onratechange
+PASS Window attribute: onreset
+PASS Window attribute: onresize
+PASS Window attribute: onscroll
+PASS Window attribute: onseeked
+PASS Window attribute: onseeking
+PASS Window attribute: onselect
+PASS Window attribute: onstalled
+PASS Window attribute: onstorage
+PASS Window attribute: onsubmit
+PASS Window attribute: onsuspend
+PASS Window attribute: ontimeupdate
+PASS Window attribute: onunload
+PASS Window attribute: onvolumechange
+PASS Window attribute: onwaiting
+PASS Window unforgeable attribute: window
+PASS Window unforgeable attribute: document
+PASS Window unforgeable attribute: location
+PASS Window unforgeable attribute: top
+PASS Window replaceable attribute: self
+PASS Window replaceable attribute: locationbar
+PASS Window replaceable attribute: menubar
+PASS Window replaceable attribute: personalbar
+PASS Window replaceable attribute: scrollbars
+PASS Window replaceable attribute: statusbar
+PASS Window replaceable attribute: toolbar
+PASS Window replaceable attribute: frames
+PASS Window replaceable attribute: parent
+PASS Window replaceable attribute: external
+PASS Window replaceable attribute: length
+PASS Window replaceable attribute: origin
+PASS Window replaceable attribute: screen
+PASS Window replaceable attribute: scrollX
+PASS Window replaceable attribute: scrollY
+PASS Window replaceable attribute: pageXOffset
+PASS Window replaceable attribute: pageYOffset
+PASS Window replaceable attribute: innerWidth
+PASS Window replaceable attribute: innerHeight
+PASS Window replaceable attribute: screenLeft
+PASS Window replaceable attribute: screenTop
+PASS Window replaceable attribute: screenX
+PASS Window replaceable attribute: screenY
+PASS Window replaceable attribute: outerWidth
+PASS Window replaceable attribute: outerHeight
+PASS Window replaceable attribute: devicePixelRatio
+PASS constructor
+PASS Window readonly getter: window
+PASS Window readonly getter in iframe: window
+FAIL Window readonly getter in detached iframe: window _eval is not a function
+PASS Window readonly getter: self
+PASS Window readonly getter in iframe: self
+FAIL Window readonly getter in detached iframe: self _eval is not a function
+PASS Window readonly getter: frames
+PASS Window readonly getter in iframe: frames
+FAIL Window readonly getter in detached iframe: frames _eval is not a function
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
new file mode 100644
index 0000000..3ddcf6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/html/browsers/the-window-object/window-properties.https-expected.txt
@@ -0,0 +1,186 @@
+This is a testharness.js-based test.
+Found 182 tests; 179 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Value Properties of the Global Object
+PASS Value Property: NaN
+PASS Value Property: Infinity
+PASS Value Property: undefined
+PASS Function Properties of the Global Object
+PASS Function Property: eval
+PASS Function Property: parseInt
+PASS Function Property: parseFloat
+PASS Function Property: isNaN
+PASS Function Property: isFinite
+PASS URI Handling Function Properties
+PASS URI Handling Function Property: decodeURI
+PASS URI Handling Function Property: decodeURIComponent
+PASS URI Handling Function Property: encodeURI
+PASS URI Handling Function Property: encodeURIComponent
+PASS Constructor Properties of the Global Object
+PASS Constructor Property: Object
+PASS Constructor Property: Function
+PASS Constructor Property: Array
+PASS Constructor Property: String
+PASS Constructor Property: Boolean
+PASS Constructor Property: Number
+PASS Constructor Property: Date
+PASS Constructor Property: RegExp
+PASS Constructor Property: Error
+PASS Constructor Property: EvalError
+PASS Constructor Property: RangeError
+PASS Constructor Property: ReferenceError
+PASS Constructor Property: SyntaxError
+PASS Constructor Property: TypeError
+PASS Constructor Property: URIError
+PASS Other Properties of the Global Object
+PASS Other Property: Math
+PASS Other Property: JSON
+PASS EventTarget interface
+PASS EventTarget method: addEventListener
+PASS EventTarget method: removeEventListener
+PASS EventTarget method: dispatchEvent
+PASS Window interface
+PASS Window method: close
+PASS Window method: stop
+PASS Window method: focus
+PASS Window method: blur
+PASS Window method: open
+PASS Window method: alert
+PASS Window method: confirm
+PASS Window method: prompt
+PASS Window method: print
+PASS Window method: postMessage
+PASS Window method: btoa
+PASS Window method: atob
+PASS Window method: setTimeout
+PASS Window method: clearTimeout
+PASS Window method: setInterval
+PASS Window method: clearInterval
+PASS Window method: queueMicrotask
+PASS Window method: createImageBitmap
+PASS Window method: getSelection
+PASS Window method: getComputedStyle
+PASS Window method: matchMedia
+PASS Window method: moveBy
+PASS Window method: moveTo
+PASS Window method: resizeBy
+PASS Window method: resizeTo
+PASS Window method: scroll
+PASS Window method: scrollTo
+PASS Window method: scrollBy
+PASS Window readonly attribute: history
+PASS Window readonly attribute: frameElement
+PASS Window readonly attribute: navigator
+PASS Window readonly attribute: sessionStorage
+PASS Window readonly attribute: localStorage
+PASS Window attribute: name
+PASS Window attribute: status
+PASS Window attribute: opener
+PASS Window attribute: onabort
+PASS Window attribute: onafterprint
+PASS Window attribute: onbeforeprint
+PASS Window attribute: onbeforeunload
+PASS Window attribute: onblur
+PASS Window attribute: oncancel
+PASS Window attribute: oncanplay
+PASS Window attribute: oncanplaythrough
+PASS Window attribute: onchange
+PASS Window attribute: onclick
+PASS Window attribute: onclose
+PASS Window attribute: oncontextmenu
+PASS Window attribute: oncuechange
+PASS Window attribute: ondblclick
+PASS Window attribute: ondrag
+PASS Window attribute: ondragend
+PASS Window attribute: ondragenter
+PASS Window attribute: ondragleave
+PASS Window attribute: ondragover
+PASS Window attribute: ondragstart
+PASS Window attribute: ondrop
+PASS Window attribute: ondurationchange
+PASS Window attribute: onemptied
+PASS Window attribute: onended
+PASS Window attribute: onerror
+PASS Window attribute: onfocus
+PASS Window attribute: onhashchange
+PASS Window attribute: oninput
+PASS Window attribute: oninvalid
+PASS Window attribute: onkeydown
+PASS Window attribute: onkeypress
+PASS Window attribute: onkeyup
+PASS Window attribute: onload
+PASS Window attribute: onloadeddata
+PASS Window attribute: onloadedmetadata
+PASS Window attribute: onloadstart
+PASS Window attribute: onmessage
+PASS Window attribute: onmousedown
+PASS Window attribute: onmousemove
+PASS Window attribute: onmouseout
+PASS Window attribute: onmouseover
+PASS Window attribute: onmouseup
+PASS Window attribute: onmousewheel
+PASS Window attribute: onoffline
+PASS Window attribute: ononline
+PASS Window attribute: onpause
+PASS Window attribute: onplay
+PASS Window attribute: onplaying
+PASS Window attribute: onpagehide
+PASS Window attribute: onpageshow
+PASS Window attribute: onpopstate
+PASS Window attribute: onprogress
+PASS Window attribute: onratechange
+PASS Window attribute: onreset
+PASS Window attribute: onresize
+PASS Window attribute: onscroll
+PASS Window attribute: onseeked
+PASS Window attribute: onseeking
+PASS Window attribute: onselect
+PASS Window attribute: onstalled
+PASS Window attribute: onstorage
+PASS Window attribute: onsubmit
+PASS Window attribute: onsuspend
+PASS Window attribute: ontimeupdate
+PASS Window attribute: onunload
+PASS Window attribute: onvolumechange
+PASS Window attribute: onwaiting
+PASS Window unforgeable attribute: window
+PASS Window unforgeable attribute: document
+PASS Window unforgeable attribute: location
+PASS Window unforgeable attribute: top
+PASS Window replaceable attribute: self
+PASS Window replaceable attribute: locationbar
+PASS Window replaceable attribute: menubar
+PASS Window replaceable attribute: personalbar
+PASS Window replaceable attribute: scrollbars
+PASS Window replaceable attribute: statusbar
+PASS Window replaceable attribute: toolbar
+PASS Window replaceable attribute: frames
+PASS Window replaceable attribute: parent
+PASS Window replaceable attribute: external
+PASS Window replaceable attribute: length
+PASS Window replaceable attribute: origin
+PASS Window replaceable attribute: screen
+PASS Window replaceable attribute: scrollX
+PASS Window replaceable attribute: scrollY
+PASS Window replaceable attribute: pageXOffset
+PASS Window replaceable attribute: pageYOffset
+PASS Window replaceable attribute: innerWidth
+PASS Window replaceable attribute: innerHeight
+PASS Window replaceable attribute: screenLeft
+PASS Window replaceable attribute: screenTop
+PASS Window replaceable attribute: screenX
+PASS Window replaceable attribute: screenY
+PASS Window replaceable attribute: outerWidth
+PASS Window replaceable attribute: outerHeight
+PASS Window replaceable attribute: devicePixelRatio
+PASS constructor
+PASS Window readonly getter: window
+PASS Window readonly getter in iframe: window
+FAIL Window readonly getter in detached iframe: window assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: self
+PASS Window readonly getter in iframe: self
+FAIL Window readonly getter in detached iframe: self assert_equals: expected object "[object Window]" but got null
+PASS Window readonly getter: frames
+PASS Window readonly getter in iframe: frames
+FAIL Window readonly getter in detached iframe: frames assert_equals: expected object "[object Window]" but got null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
deleted file mode 100644
index 80ddfd4..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL DTLS transport goes to connected state promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL close() causes the local transport to close immediately promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL close() causes the other end's DTLS transport to close promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'sender')"
-FAIL stop bundled transceiver retains dtls transport state promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/README.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/README.txt
deleted file mode 100644
index 9e16327..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# This suite runs external/wpt/webrtc/ with
-# --disable-features=RTCUnifiedPlanByDefault.
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
index 9f46719..71ff734 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
@@ -406,7 +406,8 @@
   EXPECT_TRUE(thread_map->empty());
 }
 
-TEST(ProcessReaderMac, SelfSeveralThreads) {
+// TODO(crbug.com/1319307): Test is failing on Mac. Re-enable it.
+TEST(ProcessReaderMac, DISABLED_SelfSeveralThreads) {
   // Set up the ProcessReaderMac here, before any other threads are running.
   // This tests that the threads it returns are lazily initialized as a snapshot
   // of the threads at the time of the first call to Threads(), and not at the
@@ -609,7 +610,8 @@
   size_t thread_count_;
 };
 
-TEST(ProcessReaderMac, ChildOneThread) {
+// TODO(crbug.com/1319307): Test is failing on Mac. Re-enable it.
+TEST(ProcessReaderMac, DISABLED_ChildOneThread) {
   // The main thread plus zero child threads equals one thread.
   constexpr size_t kChildThreads = 0;
   ProcessReaderThreadedChild process_reader_threaded_child("ChildOneThread",
@@ -618,7 +620,7 @@
 }
 
 // TODO(crbug.com/1319307): Test is failing on Mac. Re-enable it.
-TEST(ProcessReaderMac, ChildSeveralThreads) {
+TEST(ProcessReaderMac, DISABLED_ChildSeveralThreads) {
   constexpr size_t kChildThreads = 64;
   ProcessReaderThreadedChild process_reader_threaded_child(
       "ChildSeveralThreads", kChildThreads);
@@ -1028,7 +1030,9 @@
   bool ensure_cl_kernels_success_;
 };
 
-TEST(ProcessReaderMac, ChildModules) {
+// Disabled to investigate crbug.com/1268776.
+// TODO(crbug.com/1268776): Re-enable or remove if no longer relevant.
+TEST(ProcessReaderMac, DISABLED_ChildModules) {
   ScopedOpenCLNoOpKernel ensure_cl_kernels;
   ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6162621..eaf54e89 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -39292,7 +39292,7 @@
   <int value="2589" label="CursorImageLE32x32"/>
   <int value="2590" label="CursorImageGT32x32"/>
   <int value="2591"
-      label="RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"/>
+      label="OBSOLETE_RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"/>
   <int value="2592" label="ResizeObserver_Constructor"/>
   <int value="2593" label="Collator"/>
   <int value="2594" label="NumberFormat"/>
@@ -40541,7 +40541,7 @@
   <int value="3771" label="AddressSpaceUnknownSecureContextNavigatedToPrivate"/>
   <int value="3772"
       label="AddressSpaceUnknownNonSecureContextNavigatedToPrivate"/>
-  <int value="3773" label="RTCPeerConnectionSdpSemanticsPlanB"/>
+  <int value="3773" label="OBSOLETE_RTCPeerConnectionSdpSemanticsPlanB"/>
   <int value="3774" label="FetchRespondWithNoResponseWithUsedRequestBody"/>
   <int value="3775" label="V8TCPSocket_Close_Method"/>
   <int value="3776" label="V8TCPSocket_Readable_AttributeGetter"/>
@@ -40553,10 +40553,11 @@
   <int value="3782" label="OBSOLETE_V8HTMLPopupElement_Show_Method"/>
   <int value="3783" label="OBSOLETE_V8HTMLPopupElement_Hide_Method"/>
   <int value="3784" label="OBSOLETE_WindowOpenWithAdditionalBoolParameter"/>
-  <int value="3785" label="RTCPeerConnectionConstructedWithPlanB"/>
-  <int value="3786" label="RTCPeerConnectionConstructedWithUnifiedPlan"/>
-  <int value="3787" label="RTCPeerConnectionUsingComplexPlanB"/>
-  <int value="3788" label="RTCPeerConnectionUsingComplexUnifiedPlan"/>
+  <int value="3785" label="OBSOLETE_RTCPeerConnectionConstructedWithPlanB"/>
+  <int value="3786"
+      label="OBSOLETE_RTCPeerConnectionConstructedWithUnifiedPlan"/>
+  <int value="3787" label="OBSOLETE_RTCPeerConnectionUsingComplexPlanB"/>
+  <int value="3788" label="OBSOLETE_RTCPeerConnectionUsingComplexUnifiedPlan"/>
   <int value="3789" label="WindowScreenIsExtended"/>
   <int value="3790" label="WindowScreenChange"/>
   <int value="3791" label="XRWebGLDepthInformationTextureAttribute"/>
@@ -40705,7 +40706,7 @@
   <int value="3932" label="SpeculationRules"/>
   <int value="3933" label="V8AbortSignal_Abort_Method"/>
   <int value="3934" label="SelectionBackgroundColorInversion"/>
-  <int value="3935" label="RTCPeerConnectionPlanBThrewAnException"/>
+  <int value="3935" label="OBSOLETE_RTCPeerConnectionPlanBThrewAnException"/>
   <int value="3936" label="HTMLRootContained"/>
   <int value="3937" label="HTMLBodyContained"/>
   <int value="3938" label="XRFrameGetJointPose"/>
@@ -60797,6 +60798,7 @@
   <int value="946185008" label="EnableAppDataSearch:disabled"/>
   <int value="946688335" label="OmniboxSpareRenderer:disabled"/>
   <int value="946998724" label="SiteIsolationForGuests:enabled"/>
+  <int value="947323794" label="BruschettaAlphaMigrate:disabled"/>
   <int value="948085015" label="WebFilterInterstitialRefresh:disabled"/>
   <int value="948351976" label="WallpaperWebUI:disabled"/>
   <int value="948521531" label="StylusHandwriting:disabled"/>
@@ -61095,6 +61097,7 @@
   <int value="1133635187" label="force-gpu-rasterization"/>
   <int value="1134969852" label="NtpPhotosModuleCustomizedOptInTitle:enabled"/>
   <int value="1135728116" label="PluginVmShowCameraPermissions:enabled"/>
+  <int value="1135893278" label="BruschettaAlphaMigrate:enabled"/>
   <int value="1137878410" label="DragUnpinnedAppToPin:enabled"/>
   <int value="1138345742" label="PreferConstantFrameRate:disabled"/>
   <int value="1138349838" label="EnableMDRoundedCornersOnDialogs:disabled"/>
@@ -71439,6 +71442,17 @@
   <int value="0" label="0x00000000 - STATUS_SUCCESS"/>
 </enum>
 
+<enum name="NudgeCatalogName">
+<!-- Defined in ash/constants/notifier_catalogs.h -->
+
+  <int value="0" label="Test Catalog Name"/>
+  <int value="1" label="Dictation"/>
+  <int value="2" label="Multipaste"/>
+  <int value="3" label="Dark Light Mode"/>
+  <int value="4" label="Autozoom"/>
+  <int value="5" label="Adaptive Charging"/>
+</enum>
+
 <enum name="NukeProfileResult">
   <int value="0" label="Success (1st attempt)"/>
   <int value="1" label="Success (2nd attempt)"/>
@@ -94824,6 +94838,8 @@
 </enum>
 
 <enum name="ToastCatalogName">
+<!-- Defined in ash/constants/notifier_catalogs.h -->
+
   <int value="0" label="Virtual Desks Limit Max"/>
   <int value="1" label="Virtual Desks Limit Min"/>
   <int value="2" label="Assistant Error"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 9efb0525..eb72fea 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -2961,6 +2961,16 @@
   </token>
 </histogram>
 
+<histogram name="Ash.NotifierFramework.Nudge.ShownCount"
+    enum="NudgeCatalogName" expires_after="2023-03-15">
+  <owner>kradtke@chromium.org</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    Tracks the number of times a specific nudge is shown to users. This metric
+    is recorded every time a nudge is shown.
+  </summary>
+</histogram>
+
 <histogram name="Ash.NotifierFramework.Toast.Dismissed.{TimeRange}"
     enum="ToastCatalogName" expires_after="2023-02-10">
   <owner>kradtke@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 156fbf9..1cc0f0a 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -156,6 +156,45 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.BackgroundTokenizer.AverageTokensAvailablePerCall"
+    units="Average number of tokens" expires_after="2023-01-01">
+  <owner>sky@chromium.org</owner>
+  <owner>swarm-team@chromium.org</owner>
+  <summary>
+    Records the average number of tokens available to the main thread when
+    BackgroundHTMLTokenProducer::NextParseResults() is called and the background
+    parser did not reach the end of the input. This is recorded for main frame
+    document parsing when the feature kThreadedHtmlTokenizer is enabled. This is
+    recorded when html document parsing completes.
+  </summary>
+</histogram>
+
+<histogram
+    name="Blink.BackgroundTokenizer.AverageTokensAvailablePerCallWhenEndOfInputReached"
+    units="Average number of tokens" expires_after="2023-01-01">
+  <owner>sky@chromium.org</owner>
+  <owner>swarm-team@chromium.org</owner>
+  <summary>
+    Records the average number of tokens available to the main thread when
+    BackgroundHTMLTokenProducer::NextParseResults() is called and the background
+    parser reached the end of the input. This is recorded for main frame
+    document parsing when the feature kThreadedHtmlTokenizer is enabled. This is
+    recorded when html document parsing completes.
+  </summary>
+</histogram>
+
+<histogram name="Blink.BackgroundTokenizer.DidCompleteSuccessfully"
+    units="Boolean" expires_after="2023-01-01">
+  <owner>sky@chromium.org</owner>
+  <owner>swarm-team@chromium.org</owner>
+  <summary>
+    Records whether background html token production completed successfully.
+    This is recorded when html document parsing completes. This is only recorded
+    if the feature kThreadedHtmlTokenizer is enabled and the main frame document
+    is being parsed.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Canvas.2DLayerBridge.WillReadFrequently"
     enum="BooleanWillReadFrequently" expires_after="2023-04-01">
   <owner>aaronhk@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index 7639e63..a162832 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -306,7 +306,7 @@
 </histogram>
 
 <histogram name="FileBrowser.FolderShortcut.Add" units="Shortcuts"
-    expires_after="M108">
+    expires_after="2023-09-01">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -326,7 +326,7 @@
 </histogram>
 
 <histogram name="FileBrowser.FolderShortcut.Navigate" units="Navigations"
-    expires_after="M108">
+    expires_after="2023-09-01">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -336,7 +336,7 @@
 </histogram>
 
 <histogram name="FileBrowser.FolderShortcut.Remove" units="Shortcuts"
-    expires_after="M108">
+    expires_after="2023-09-01">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -496,6 +496,9 @@
 
 <histogram name="FileBrowser.MediaImport.Duplicates" units="count"
     expires_after="M108">
+  <obsolete>
+    Became obsolete on 2022-08-18 as this feature is no longer in existence.
+  </obsolete>
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</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 1498714..08bee394 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,16 +5,16 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "71a0050911c5e42776621a3f7a0be52fdffe7b03",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/c16f31c6da7523528ae09808c3427fb395142c94/trace_processor_shell.exe"
+            "hash": "2643f7ff8a6db78346d4bdd61c6077c9507fee3f",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/5b39b1050966a57df482206b84e96e31e602f48b/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": "3a30ef339879b2d94afe0549d924c9c02248d25e",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/c16f31c6da7523528ae09808c3427fb395142c94/trace_processor_shell"
+            "hash": "65e00b832831bd94638cc93f391882a1539e5080",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/5b39b1050966a57df482206b84e96e31e602f48b/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index 5020097..3584fd5 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -38,7 +38,7 @@
 constexpr int kOutputLevel = 4;
 
 base::Feature kCALayerTreeOptimization{"CALayerTreeOptimization",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
 void RecordIOSurfaceHistograms(
     int changed_io_surfaces_during_commit,
diff --git a/ui/base/ime/ash/input_method_ash_unittest.cc b/ui/base/ime/ash/input_method_ash_unittest.cc
index 5eb42d06..bee6c3c 100644
--- a/ui/base/ime/ash/input_method_ash_unittest.cc
+++ b/ui/base/ime/ash/input_method_ash_unittest.cc
@@ -233,8 +233,8 @@
     IMEBridge::Get()->SetCandidateWindowHandler(
         mock_ime_candidate_window_handler_.get());
 
-    ime_ = std::make_unique<TestableInputMethodAsh>(this);
-    ime_->SetFocusedTextInputClient(this);
+    input_method_ash_ = std::make_unique<TestableInputMethodAsh>(this);
+    input_method_ash_->SetFocusedTextInputClient(this);
 
     // InputMethodManager owns and delete it in InputMethodManager::Shutdown().
     input_method_manager_ = new TestInputMethodManager();
@@ -242,9 +242,9 @@
   }
 
   void TearDown() override {
-    if (ime_.get())
-      ime_->SetFocusedTextInputClient(nullptr);
-    ime_.reset();
+    if (input_method_ash_.get())
+      input_method_ash_->SetFocusedTextInputClient(nullptr);
+    input_method_ash_.reset();
     IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
     IMEBridge::Get()->SetCandidateWindowHandler(nullptr);
     mock_ime_engine_handler_.reset();
@@ -350,7 +350,7 @@
   }
 
  protected:
-  std::unique_ptr<TestableInputMethodAsh> ime_;
+  std::unique_ptr<TestableInputMethodAsh> input_method_ash_;
 
   // Copy of the dispatched key event.
   ui::KeyEvent dispatched_key_event_;
@@ -410,35 +410,35 @@
 }
 
 TEST_F(InputMethodAshTest, GetTextInputClient) {
-  EXPECT_EQ(this, ime_->GetTextInputClient());
-  ime_->SetFocusedTextInputClient(nullptr);
-  EXPECT_EQ(nullptr, ime_->GetTextInputClient());
+  EXPECT_EQ(this, input_method_ash_->GetTextInputClient());
+  input_method_ash_->SetFocusedTextInputClient(nullptr);
+  EXPECT_EQ(nullptr, input_method_ash_->GetTextInputClient());
 }
 
 TEST_F(InputMethodAshTest, GetInputTextType_WithoutFocusedClient) {
-  EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
-  ime_->SetFocusedTextInputClient(nullptr);
+  EXPECT_EQ(TEXT_INPUT_TYPE_NONE, input_method_ash_->GetTextInputType());
+  input_method_ash_->SetFocusedTextInputClient(nullptr);
   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   // The OnTextInputTypeChanged() call above should be ignored since |this| is
   // not the current focused client.
-  EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
+  EXPECT_EQ(TEXT_INPUT_TYPE_NONE, input_method_ash_->GetTextInputType());
 
-  ime_->SetFocusedTextInputClient(this);
-  ime_->OnTextInputTypeChanged(this);
-  EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
+  input_method_ash_->SetFocusedTextInputClient(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
+  EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, input_method_ash_->GetTextInputType());
 }
 
 TEST_F(InputMethodAshTest, OnWillChangeFocusedClientClearAutocorrectRange) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->SetFocusedTextInputClient(this);
-  ime_->CommitText(
+  input_method_ash_->SetFocusedTextInputClient(this);
+  input_method_ash_->CommitText(
       u"hello",
       TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
-  ime_->SetAutocorrectRange(gfx::Range(0, 5));
+  input_method_ash_->SetAutocorrectRange(gfx::Range(0, 5));
   EXPECT_EQ(gfx::Range(0, 5), this->GetAutocorrectRange());
 
-  ime_->SetFocusedTextInputClient(nullptr);
+  input_method_ash_->SetFocusedTextInputClient(nullptr);
   EXPECT_EQ(gfx::Range(), this->GetAutocorrectRange());
 }
 
@@ -449,7 +449,7 @@
   EXPECT_EQ(0U, on_input_method_changed_call_count_);
   // Click a text input form.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   // Since a form has focus, IBusClient::FocusIn() should be called.
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(1,
@@ -465,7 +465,7 @@
 TEST_F(InputMethodAshTest, FocusIn_Password) {
   EXPECT_EQ(0U, on_input_method_changed_call_count_);
   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   // InputMethodEngine::FocusIn() should be called even for password field.
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(1U, on_input_method_changed_call_count_);
@@ -474,11 +474,11 @@
 // Confirm that IBusClient::FocusOut is called as expected.
 TEST_F(InputMethodAshTest, FocusOut_None) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
   input_type_ = TEXT_INPUT_TYPE_NONE;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
 }
@@ -486,11 +486,11 @@
 // Confirm that IBusClient::FocusOut is called as expected.
 TEST_F(InputMethodAshTest, FocusOut_Password) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
 }
@@ -507,7 +507,7 @@
 
   input_type_ = TEXT_INPUT_TYPE_TEXT;
   input_mode_ = TEXT_INPUT_MODE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   // Confirm that only FocusIn is called, the TextInputType is TEXT and the
   // TextInputMode is LATIN..
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
@@ -518,7 +518,7 @@
             mock_ime_engine_handler_->last_text_input_context().mode);
 
   input_mode_ = TEXT_INPUT_MODE_SEARCH;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   // Confirm that both FocusIn and FocusOut are called for mode change.
   EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
@@ -528,7 +528,7 @@
             mock_ime_engine_handler_->last_text_input_context().mode);
 
   input_type_ = TEXT_INPUT_TYPE_URL;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   // Confirm that both FocusIn and FocusOut are called and the TextInputType is
   // changed to URL.
   EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
@@ -539,11 +539,11 @@
             mock_ime_engine_handler_->last_text_input_context().mode);
 
   // Confirm that FocusOut is called when set focus to NULL client.
-  ime_->SetFocusedTextInputClient(nullptr);
+  input_method_ash_->SetFocusedTextInputClient(nullptr);
   EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
   // Confirm that FocusIn is called when set focus to this client.
-  ime_->SetFocusedTextInputClient(this);
+  input_method_ash_->SetFocusedTextInputClient(this);
   EXPECT_EQ(4, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
 }
@@ -551,19 +551,19 @@
 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
 TEST_F(InputMethodAshTest, OnCaretBoundsChanged) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   EXPECT_EQ(1,
             mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
   caret_bounds_ = gfx::Rect(1, 2, 3, 4);
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
   EXPECT_EQ(2,
             mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
   caret_bounds_ = gfx::Rect(0, 2, 3, 4);
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
   EXPECT_EQ(3,
             mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
   caret_bounds_ = gfx::Rect(0, 2, 3, 4);  // unchanged
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
   // Current InputMethodAsh implementation performs the IPC
   // regardless of the bounds are changed or not.
   EXPECT_EQ(4,
@@ -577,8 +577,8 @@
   CompositionText ash_composition_text;
   ash_composition_text.text = kSampleAsciiText;
 
-  CompositionText composition_text =
-      ime_->ExtractCompositionText(ash_composition_text, kCursorPos);
+  CompositionText composition_text = input_method_ash_->ExtractCompositionText(
+      ash_composition_text, kCursorPos);
   EXPECT_EQ(kSampleAsciiText, composition_text.text);
   // If there is no selection, |selection| represents cursor position.
   EXPECT_EQ(kCursorPos, composition_text.selection.start());
@@ -618,7 +618,7 @@
   composition_text.ime_text_spans.push_back(underline);
 
   CompositionText composition_text2 =
-      ime_->ExtractCompositionText(composition_text, kCursorPos);
+      input_method_ash_->ExtractCompositionText(composition_text, kCursorPos);
   EXPECT_EQ(kSampleText, composition_text2.text);
   // If there is no selection, |selection| represents cursor position.
   EXPECT_EQ(kCursorPos, composition_text2.selection.start());
@@ -650,7 +650,7 @@
   composition_text.ime_text_spans.push_back(underline);
 
   CompositionText composition_text2 =
-      ime_->ExtractCompositionText(composition_text, kCursorPos);
+      input_method_ash_->ExtractCompositionText(composition_text, kCursorPos);
   EXPECT_EQ(kSampleText, composition_text2.text);
   // If there is no selection, |selection| represents cursor position.
   EXPECT_EQ(kCursorPos, composition_text2.selection.start());
@@ -683,7 +683,7 @@
   composition_text.ime_text_spans.push_back(underline);
 
   CompositionText composition_text2 =
-      ime_->ExtractCompositionText(composition_text, kCursorPos);
+      input_method_ash_->ExtractCompositionText(composition_text, kCursorPos);
   EXPECT_EQ(kSampleText, composition_text2.text);
   EXPECT_EQ(kCursorPos, composition_text2.selection.start());
   EXPECT_EQ(kCursorPos, composition_text2.selection.end());
@@ -708,7 +708,7 @@
   composition_text.selection.set_end(4UL);
 
   CompositionText composition_text2 =
-      ime_->ExtractCompositionText(composition_text, kCursorPos);
+      input_method_ash_->ExtractCompositionText(composition_text, kCursorPos);
   EXPECT_EQ(kSampleText, composition_text2.text);
   EXPECT_EQ(kCursorPos, composition_text2.selection.start());
   EXPECT_EQ(kCursorPos, composition_text2.selection.end());
@@ -736,7 +736,7 @@
   composition_text.selection.set_end(4UL);
 
   CompositionText composition_text2 =
-      ime_->ExtractCompositionText(composition_text, kCursorPos);
+      input_method_ash_->ExtractCompositionText(composition_text, kCursorPos);
   EXPECT_EQ(kSampleText, composition_text2.text);
   // If the cursor position is same as selection bounds, selection start
   // position become opposit side of selection from cursor.
@@ -767,7 +767,7 @@
   composition_text.selection.set_end(kCursorPos);
 
   CompositionText composition_text2 =
-      ime_->ExtractCompositionText(composition_text, kCursorPos);
+      input_method_ash_->ExtractCompositionText(composition_text, kCursorPos);
   EXPECT_EQ(kSampleText, composition_text2.text);
   // If the cursor position is same as selection bounds, selection start
   // position become opposit side of selection from cursor.
@@ -791,7 +791,7 @@
 TEST_F(InputMethodAshTest, SurroundingText_NoSelectionTest) {
   // Click a text input form.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Set the TextInputClient behaviors.
   surrounding_text_ = u"abcdef";
@@ -801,7 +801,7 @@
   // Set the verifier for SetSurroundingText mock call.
   SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
 
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
 
   // Check the call count.
   EXPECT_EQ(1, mock_ime_engine_handler_->set_surrounding_text_call_count());
@@ -814,7 +814,7 @@
 TEST_F(InputMethodAshTest, SurroundingText_SelectionTest) {
   // Click a text input form.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Set the TextInputClient behaviors.
   surrounding_text_ = u"abcdef";
@@ -824,7 +824,7 @@
   // Set the verifier for SetSurroundingText mock call.
   SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
 
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
 
   // Check the call count.
   EXPECT_EQ(1, mock_ime_engine_handler_->set_surrounding_text_call_count());
@@ -837,14 +837,14 @@
 TEST_F(InputMethodAshTest, SurroundingText_PartialText) {
   // Click a text input form.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Set the TextInputClient behaviors.
   surrounding_text_ = u"abcdefghij";
   text_range_ = gfx::Range(5, 10);
   selection_range_ = gfx::Range(7, 9);
 
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
 
   // Check the call count.
   EXPECT_EQ(1, mock_ime_engine_handler_->set_surrounding_text_call_count());
@@ -858,7 +858,7 @@
 TEST_F(InputMethodAshTest, SurroundingText_BecomeEmptyText) {
   // Click a text input form.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Set the TextInputClient behaviors.
   // If the surrounding text becomes empty, text_range become (0, 0) and
@@ -867,13 +867,13 @@
   text_range_ = gfx::Range(0, 0);
   selection_range_ = gfx::Range::InvalidRange();
 
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
 
   // Check the call count.
   EXPECT_EQ(0, mock_ime_engine_handler_->set_surrounding_text_call_count());
 
   // Should not be called twice with same condition.
-  ime_->OnCaretBoundsChanged(this);
+  input_method_ash_->OnCaretBoundsChanged(this);
   EXPECT_EQ(0, mock_ime_engine_handler_->set_surrounding_text_call_count());
 }
 
@@ -893,8 +893,8 @@
     selection_range_ = gfx::Range(0, 0);
 
     input_type_ = TEXT_INPUT_TYPE_TEXT;
-    ime_->OnWillChangeFocusedClient(nullptr, this);
-    ime_->OnDidChangeFocusedClient(nullptr, this);
+    input_method_ash_->OnWillChangeFocusedClient(nullptr, this);
+    input_method_ash_->OnDidChangeFocusedClient(nullptr, this);
   }
 
   {
@@ -909,7 +909,7 @@
     selection_range_ = gfx::Range(0, 0);
 
     input_type_ = TEXT_INPUT_TYPE_EMAIL;
-    ime_->OnTextInputTypeChanged(this);
+    input_method_ash_->OnTextInputTypeChanged(this);
   }
   IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
 }
@@ -917,14 +917,14 @@
 TEST_F(InputMethodAshTest, SetCompositionRange_InvalidRange) {
   // Focus on a text field.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Insert some text and place the cursor.
   surrounding_text_ = u"abc";
   text_range_ = gfx::Range(0, 3);
   selection_range_ = gfx::Range(1, 1);
 
-  EXPECT_FALSE(ime_->SetCompositionRange(0, 4, {}));
+  EXPECT_FALSE(input_method_ash_->SetCompositionRange(0, 4, {}));
   EXPECT_EQ(0U, composition_text_.text.length());
 }
 
@@ -948,10 +948,10 @@
 TEST_F(InputMethodAshTest, ConfirmCompositionText_NoComposition) {
   // Focus on a text field.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
-  ime_->ConfirmCompositionText(/* reset_engine */ true,
-                               /* keep_selection */ false);
+  input_method_ash_->ConfirmCompositionText(/* reset_engine */ true,
+                                            /* keep_selection */ false);
 
   EXPECT_TRUE(confirmed_text_.text.empty());
   EXPECT_TRUE(composition_text_.text.empty());
@@ -960,13 +960,13 @@
 TEST_F(InputMethodAshTest, ConfirmCompositionText_SetComposition) {
   // Focus on a text field.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   CompositionText composition_text;
   composition_text.text = u"hello";
   SetCompositionText(composition_text);
-  ime_->ConfirmCompositionText(/* reset_engine */ true,
-                               /* keep_selection */ false);
+  input_method_ash_->ConfirmCompositionText(/* reset_engine */ true,
+                                            /* keep_selection */ false);
 
   EXPECT_EQ(u"hello", confirmed_text_.text);
   EXPECT_TRUE(composition_text_.text.empty());
@@ -975,16 +975,16 @@
 TEST_F(InputMethodAshTest, ConfirmCompositionText_SetCompositionRange) {
   // Focus on a text field.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Place some text.
   surrounding_text_ = u"abc";
   text_range_ = gfx::Range(0, 3);
 
   // "abc" is in composition. Put the two characters in composition.
-  ime_->SetCompositionRange(0, 2, {});
-  ime_->ConfirmCompositionText(/* reset_engine */ true,
-                               /* keep_selection */ false);
+  input_method_ash_->SetCompositionRange(0, 2, {});
+  input_method_ash_->ConfirmCompositionText(/* reset_engine */ true,
+                                            /* keep_selection */ false);
 
   EXPECT_EQ(u"ab", confirmed_text_.text);
   EXPECT_TRUE(composition_text_.text.empty());
@@ -1007,8 +1007,8 @@
 
   // Do key event.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
-  ime_->DispatchKeyEvent(&event);
+  input_method_ash_->OnTextInputTypeChanged(this);
+  input_method_ash_->DispatchKeyEvent(&event);
 
   // Check before state.
   const ui::KeyEvent* key_event =
@@ -1016,9 +1016,9 @@
   EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
   EXPECT_EQ(ui::VKEY_A, key_event->key_code());
   EXPECT_EQ(kFlags, key_event->flags());
-  EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
+  EXPECT_EQ(0, input_method_ash_->process_key_event_post_ime_call_count());
 
-  static_cast<IMEInputContextHandlerInterface*>(ime_.get())
+  static_cast<IMEInputContextHandlerInterface*>(input_method_ash_.get())
       ->CommitText(
           u"A",
           TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
@@ -1029,12 +1029,12 @@
   std::move(mock_ime_engine_handler_->last_passed_callback()).Run(true);
 
   // Check the results
-  EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
+  EXPECT_EQ(1, input_method_ash_->process_key_event_post_ime_call_count());
   const ui::KeyEvent stored_event =
-      ime_->process_key_event_post_ime_args().event;
+      input_method_ash_->process_key_event_post_ime_args().event;
   EXPECT_EQ(ui::VKEY_A, stored_event.key_code());
   EXPECT_EQ(kFlags, stored_event.flags());
-  EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
+  EXPECT_TRUE(input_method_ash_->process_key_event_post_ime_args().handled);
 
   EXPECT_EQ(L'A', inserted_char_);
 }
@@ -1044,13 +1044,13 @@
 
   // Preparation
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   const int kFlags = ui::EF_SHIFT_DOWN;
   ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
 
   // Do key event.
-  ime_->DispatchKeyEvent(&event);
+  input_method_ash_->DispatchKeyEvent(&event);
   const ui::KeyEvent* key_event =
       mock_ime_engine_handler_->last_processed_key_event();
   EXPECT_EQ(ui::VKEY_B, key_event->key_code());
@@ -1062,7 +1062,7 @@
   // Do key event again.
   ui::KeyEvent event2(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
 
-  ime_->DispatchKeyEvent(&event2);
+  input_method_ash_->DispatchKeyEvent(&event2);
   const ui::KeyEvent* key_event2 =
       mock_ime_engine_handler_->last_processed_key_event();
   EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
@@ -1070,11 +1070,11 @@
 
   // Check before state.
   EXPECT_EQ(2, mock_ime_engine_handler_->process_key_event_call_count());
-  EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
+  EXPECT_EQ(0, input_method_ash_->process_key_event_post_ime_call_count());
 
   CompositionText comp;
   comp.text = u"B";
-  (static_cast<IMEInputContextHandlerInterface*>(ime_.get()))
+  (static_cast<IMEInputContextHandlerInterface*>(input_method_ash_.get()))
       ->UpdateCompositionText(comp, comp.text.length(), true);
 
   EXPECT_EQ(0, composition_text_.text[0]);
@@ -1085,22 +1085,23 @@
   EXPECT_EQ(comp.text, composition_text_.text);
 
   // Check the results for first key event.
-  EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
-  ui::KeyEvent stored_event = ime_->process_key_event_post_ime_args().event;
+  EXPECT_EQ(1, input_method_ash_->process_key_event_post_ime_call_count());
+  ui::KeyEvent stored_event =
+      input_method_ash_->process_key_event_post_ime_args().event;
   EXPECT_EQ(ui::VKEY_B, stored_event.key_code());
   EXPECT_EQ(kFlags, stored_event.flags());
-  EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
+  EXPECT_TRUE(input_method_ash_->process_key_event_post_ime_args().handled);
   EXPECT_EQ(0, inserted_char_);
 
   // Do callback for second key event.
   mock_ime_engine_handler_->last_passed_callback().Run(false);
 
   // Check the results for second key event.
-  EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
-  stored_event = ime_->process_key_event_post_ime_args().event;
+  EXPECT_EQ(2, input_method_ash_->process_key_event_post_ime_call_count());
+  stored_event = input_method_ash_->process_key_event_post_ime_args().event;
   EXPECT_EQ(ui::VKEY_C, stored_event.key_code());
   EXPECT_EQ(kFlags, stored_event.flags());
-  EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
+  EXPECT_FALSE(input_method_ash_->process_key_event_post_ime_args().handled);
 
   EXPECT_EQ(L'C', inserted_char_);
 }
@@ -1108,13 +1109,13 @@
 TEST_F(InputMethodAshKeyEventTest, StopPropagationTest) {
   // Preparation
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   // Do key event with event being stopped propagation.
   stop_propagation_post_ime_ = true;
   ui::KeyEvent eventA(ui::ET_KEY_PRESSED, ui::VKEY_A, EF_NONE);
   eventA.set_character(L'A');
-  ime_->DispatchKeyEvent(&eventA);
+  input_method_ash_->DispatchKeyEvent(&eventA);
   mock_ime_engine_handler_->last_passed_callback().Run(false);
 
   const ui::KeyEvent* key_event =
@@ -1124,7 +1125,7 @@
 
   // Do key event with event not being stopped propagation.
   stop_propagation_post_ime_ = false;
-  ime_->DispatchKeyEvent(&eventA);
+  input_method_ash_->DispatchKeyEvent(&eventA);
   mock_ime_engine_handler_->last_passed_callback().Run(false);
 
   key_event = mock_ime_engine_handler_->last_processed_key_event();
@@ -1135,14 +1136,14 @@
 TEST_F(InputMethodAshKeyEventTest, DeadKeyPressTest) {
   // Preparation.
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
 
   ui::KeyEvent eventA(ET_KEY_PRESSED,
                       VKEY_OEM_4,  // '['
                       DomCode::BRACKET_LEFT, 0,
                       DomKey::DeadKeyFromCombiningCharacter('^'),
                       EventTimeForNow());
-  ime_->ProcessKeyEventPostIME(&eventA, true, true);
+  input_method_ash_->ProcessKeyEventPostIME(&eventA, true, true);
 
   const ui::KeyEvent& key_event = dispatched_key_event_;
 
@@ -1156,35 +1157,35 @@
 
 TEST_F(InputMethodAshKeyEventTest, JP106KeyTest) {
   ui::KeyEvent eventConvert(ET_KEY_PRESSED, VKEY_CONVERT, EF_NONE);
-  ime_->DispatchKeyEvent(&eventConvert);
+  input_method_ash_->DispatchKeyEvent(&eventConvert);
   EXPECT_FALSE(input_method_manager_->state()->is_jp_kbd());
   EXPECT_TRUE(input_method_manager_->state()->is_jp_ime());
 
   ui::KeyEvent eventNonConvert(ET_KEY_PRESSED, VKEY_NONCONVERT, EF_NONE);
-  ime_->DispatchKeyEvent(&eventNonConvert);
+  input_method_ash_->DispatchKeyEvent(&eventNonConvert);
   EXPECT_TRUE(input_method_manager_->state()->is_jp_kbd());
   EXPECT_FALSE(input_method_manager_->state()->is_jp_ime());
 
   ui::KeyEvent eventDbeSbc(ET_KEY_PRESSED, VKEY_DBE_SBCSCHAR, EF_NONE);
-  ime_->DispatchKeyEvent(&eventDbeSbc);
+  input_method_ash_->DispatchKeyEvent(&eventDbeSbc);
   EXPECT_FALSE(input_method_manager_->state()->is_jp_kbd());
   EXPECT_TRUE(input_method_manager_->state()->is_jp_ime());
 
   ui::KeyEvent eventDbeDbc(ET_KEY_PRESSED, VKEY_DBE_DBCSCHAR, EF_NONE);
-  ime_->DispatchKeyEvent(&eventDbeDbc);
+  input_method_ash_->DispatchKeyEvent(&eventDbeDbc);
   EXPECT_TRUE(input_method_manager_->state()->is_jp_kbd());
   EXPECT_FALSE(input_method_manager_->state()->is_jp_ime());
 }
 
 TEST_F(InputMethodAshKeyEventTest, SetAutocorrectRangeRunsAfterKeyEvent) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
-  ime_->CommitText(
+  input_method_ash_->OnTextInputTypeChanged(this);
+  input_method_ash_->CommitText(
       u"a", TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
 
   ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
-  ime_->DispatchKeyEvent(&event);
-  ime_->SetAutocorrectRange(gfx::Range(0, 1));
+  input_method_ash_->DispatchKeyEvent(&event);
+  input_method_ash_->SetAutocorrectRange(gfx::Range(0, 1));
   std::move(mock_ime_engine_handler_->last_passed_callback())
       .Run(/*handled=*/true);
 
@@ -1193,13 +1194,13 @@
 
 TEST_F(InputMethodAshKeyEventTest, SetAutocorrectRangeRunsAfterCommitText) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
-  ime_->OnTextInputTypeChanged(this);
+  input_method_ash_->OnTextInputTypeChanged(this);
   ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
-  ime_->DispatchKeyEvent(&event);
+  input_method_ash_->DispatchKeyEvent(&event);
 
-  ime_->CommitText(
+  input_method_ash_->CommitText(
       u"a", TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
-  ime_->SetAutocorrectRange(gfx::Range(0, 1));
+  input_method_ash_->SetAutocorrectRange(gfx::Range(0, 1));
   std::move(mock_ime_engine_handler_->last_passed_callback())
       .Run(/*handled=*/true);
 
@@ -1332,26 +1333,26 @@
   std::vector<GrammarFragment> fragments;
   fragments.emplace_back(gfx::Range(0, 1), "fake");
   fragments.emplace_back(gfx::Range(3, 10), "test");
-  ime_->AddGrammarFragments(fragments);
+  input_method_ash_->AddGrammarFragments(fragments);
   EXPECT_EQ(get_grammar_fragments(), fragments);
-  ime_->ClearGrammarFragments(gfx::Range(0, 10));
+  input_method_ash_->ClearGrammarFragments(gfx::Range(0, 10));
   EXPECT_EQ(get_grammar_fragments().size(), 0u);
 }
 
 TEST_F(InputMethodAshTest, GetsGrammarFragments) {
   input_type_ = TEXT_INPUT_TYPE_TEXT;
   GrammarFragment fragment(gfx::Range(0, 5), "fake");
-  ime_->AddGrammarFragments({fragment});
+  input_method_ash_->AddGrammarFragments({fragment});
 
-  ime_->SetEditableSelectionRange(gfx::Range(3, 3));
-  EXPECT_EQ(ime_->GetGrammarFragmentAtCursor(), fragment);
-  ime_->SetEditableSelectionRange(gfx::Range(2, 4));
-  EXPECT_EQ(ime_->GetGrammarFragmentAtCursor(), fragment);
+  input_method_ash_->SetEditableSelectionRange(gfx::Range(3, 3));
+  EXPECT_EQ(input_method_ash_->GetGrammarFragmentAtCursor(), fragment);
+  input_method_ash_->SetEditableSelectionRange(gfx::Range(2, 4));
+  EXPECT_EQ(input_method_ash_->GetGrammarFragmentAtCursor(), fragment);
 
-  ime_->SetEditableSelectionRange(gfx::Range(7, 7));
-  EXPECT_EQ(ime_->GetGrammarFragmentAtCursor(), absl::nullopt);
-  ime_->SetEditableSelectionRange(gfx::Range(4, 7));
-  EXPECT_EQ(ime_->GetGrammarFragmentAtCursor(), absl::nullopt);
+  input_method_ash_->SetEditableSelectionRange(gfx::Range(7, 7));
+  EXPECT_EQ(input_method_ash_->GetGrammarFragmentAtCursor(), absl::nullopt);
+  input_method_ash_->SetEditableSelectionRange(gfx::Range(4, 7));
+  EXPECT_EQ(input_method_ash_->GetGrammarFragmentAtCursor(), absl::nullopt);
 }
 
 }  // namespace ui
diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn
index 6f43d26..71cf8ff 100644
--- a/ui/compositor/BUILD.gn
+++ b/ui/compositor/BUILD.gn
@@ -272,6 +272,7 @@
     # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests.
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
     additional_manifest_fragments = [
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/present_view.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index 63bcaf16..c0193fb 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -608,8 +608,6 @@
           /** @type {!importer.Disposition} */ (disposition)];
       totalDeduped += count;
     }, this);
-
-    metrics.recordMediumCount('MediaImport.Duplicates', totalDeduped);
   }
 };
 
diff --git a/ui/file_manager/file_manager/externs/ts/store.js b/ui/file_manager/file_manager/externs/ts/store.js
index 18d16ba7..df5607b 100644
--- a/ui/file_manager/file_manager/externs/ts/store.js
+++ b/ui/file_manager/file_manager/externs/ts/store.js
@@ -23,6 +23,9 @@
 
   /** @param {!StoreObserver} observer */
   usubscribe(observer) {}
+
+  /** @return {!State} */
+  getState() {}
 }
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/elements/files_password_dialog.js b/ui/file_manager/file_manager/foreground/elements/files_password_dialog.js
index 246ecb7..dce0b7b 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_password_dialog.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_password_dialog.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/ui/file_manager/file_manager/foreground/elements/files_password_dialog_unittest.js b/ui/file_manager/file_manager/foreground/elements/files_password_dialog_unittest.js
index abded9dc..0fe4467 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_password_dialog_unittest.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_password_dialog_unittest.js
@@ -6,6 +6,7 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 
 import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {assertEquals, assertFalse, assertNotReached} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index a26e9bc..b1346a6 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -551,8 +551,9 @@
     "//ui/file_manager/file_manager/foreground/elements:xf_circular_progress",
     "//ui/file_manager/file_manager/foreground/elements:xf_display_panel",
     "//ui/file_manager/file_manager/foreground/elements:xf_panel_item",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
 }
 
 js_library("file_list_model") {
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index 71ddac3..65d70e4830 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -1699,7 +1699,9 @@
       this.onSearchCompleted_ = null;
     }
 
-    this.store_.dispatch(searchAction({}));
+    if (this.store_.getState()?.search?.query) {
+      this.store_.dispatch(searchAction({}));
+    }
   }
 
   /**
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 8d28f87c..8e9e86f 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
@@ -4,6 +4,7 @@
 
 import './webui_command_extender.js';
 
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {Command} from 'chrome://resources/js/cr/ui/command.js';
 import {contextMenuHandler} from 'chrome://resources/js/cr/ui/context_menu_handler.js';
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
index 48343562..ae934f4 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_input/cr_input.js';
-
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {decorate} from 'chrome://resources/js/cr/ui.m.js';
 import {Command} from 'chrome://resources/js/cr/ui/command.js';
 import {ListSelectionModel} from 'chrome://resources/js/cr/ui/list_selection_model.m.js';
diff --git a/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js b/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js
index a979af2..6b97e04 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.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 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 
 import {DialogType} from '../../../common/js/dialog_type.js';
 import {FileType} from '../../../common/js/file_type.js';
diff --git a/ui/file_manager/integration_tests/file_manager/breadcrumbs.js b/ui/file_manager/integration_tests/file_manager/breadcrumbs.js
index af62da7..2f6ed80 100644
--- a/ui/file_manager/integration_tests/file_manager/breadcrumbs.js
+++ b/ui/file_manager/integration_tests/file_manager/breadcrumbs.js
@@ -648,3 +648,38 @@
     }
   });
 };
+
+/**
+ * Test that navigating back from a sub-folder in Google Drive> Shared With Me
+ * using the breadcrumbs.
+ * Internally Shared With Me uses some of the Drive Search code, this confused
+ * the DirectoryModel clearing the search state in the Store.
+ */
+testcase.breadcrumbNavigateBackToSharedWithMe = async () => {
+  // Open Files app on Drive containing "Shared with me" file entries.
+  const sharedSubFolderName = ENTRIES.sharedWithMeDirectory.nameText;
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DRIVE, [], [ENTRIES.sharedWithMeDirectory, ENTRIES.hello]);
+
+  // Navigate to Shared with me.
+  await remoteCall.waitAndClickElement(
+      appId, '#directory-tree [entry-label="Shared with me"]');
+
+  // Wait until the breadcrumb path is updated.
+  await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Shared with me');
+
+  // Navigate to the directory within Shared with me.
+  await remoteCall.waitUntilSelected(appId, sharedSubFolderName);
+  await remoteCall.fakeKeyDown(
+      appId, '#file-list', 'Enter', false, false, false);
+
+  await remoteCall.waitUntilCurrentDirectoryIsChanged(
+      appId, `/Shared with me/${sharedSubFolderName}`);
+
+  // Navigate back using breadcrumb.
+  await remoteCall.waitAndClickElement(
+      appId, ['xf-breadcrumb', 'button[id="first"]']);
+
+  // Wait until the breadcrumb path is updated.
+  await remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Shared with me');
+};
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index dbe5663..c890f87 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -47,6 +47,7 @@
 #include "ui/ozone/platform/wayland/common/wayland_util.h"
 #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection_test_api.h"
+#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
 #include "ui/ozone/platform/wayland/host/wayland_output.h"
 #include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/host/wayland_seat.h"
@@ -1543,6 +1544,52 @@
   menu_window.reset();
 }
 
+// Verify that located events are translated correctly when the windows have
+// geometry with non-zero offset.
+// See https://crbug.com/1292486.
+TEST_P(WaylandWindowTest, ConvertEventToTarget) {
+  // This first section repeats a part of SetDecorationInsets that sets
+  // decoration insets and ensures that they have been applied.
+  constexpr gfx::Rect kMainWindowBounds{956, 556};
+  const auto kMainWindowInsets = gfx::Insets::TLBR(24, 28, 32, 28);
+  auto bounds_with_insets = kMainWindowBounds;
+  bounds_with_insets.Inset(kMainWindowInsets);
+  EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0);
+  EXPECT_CALL(*xdg_surface_, SetWindowGeometry(bounds_with_insets));
+  window_->SetDecorationInsets(&kMainWindowInsets);
+  // Setting the decoration insets does not trigger the immediate update of the
+  // window geometry.  Emulate updating the visual size (sending the frame
+  // update) for that.
+  window_->UpdateVisualSize(kMainWindowBounds.size());
+
+  Sync();
+
+  // Create a menu.
+  constexpr gfx::Rect kMenuBounds{100, 100, 80, 50};
+  MockWaylandPlatformWindowDelegate menu_window_delegate;
+  EXPECT_CALL(menu_window_delegate, GetMenuType())
+      .WillOnce(Return(MenuType::kRootContextMenu));
+  std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
+      PlatformWindowType::kMenu, widget_, kMenuBounds, &menu_window_delegate);
+  EXPECT_TRUE(menu_window);
+
+  // Now translate the event located at (0, 0) in the parent window into the
+  // coordinate system of the menu.  Its coordinates must be equal to:
+  //     -(offset of parent geometry + offset of the menu).
+  constexpr gfx::PointF kParentPoint{0, 0};
+  ui::MouseEvent event(ui::EventType::ET_MOUSE_MOVED, kParentPoint,
+                       kParentPoint, {}, ui::EF_NONE, ui::EF_NONE);
+
+  ui::Event::DispatcherApi dispatcher_api(&event);
+  dispatcher_api.set_target(window_.get());
+
+  ui::WaylandEventSource::ConvertEventToTarget(menu_window.get(), &event);
+  EXPECT_EQ(event.AsLocatedEvent()->x(),
+            -(kMenuBounds.x() + kMainWindowInsets.left()));
+  EXPECT_EQ(event.AsLocatedEvent()->y(),
+            -(kMenuBounds.y() + kMainWindowInsets.top()));
+}
+
 // Tests that the event grabber gets the events processed by its toplevel parent
 // window iff they belong to the same "family". Otherwise, events mustn't be
 // rerouted from another toplevel window to the event grabber.
diff --git a/ui/snapshot/BUILD.gn b/ui/snapshot/BUILD.gn
index 653ea22..c9732de 100644
--- a/ui/snapshot/BUILD.gn
+++ b/ui/snapshot/BUILD.gn
@@ -128,6 +128,7 @@
     test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
 
     additional_manifest_fragments = [
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/present_view.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index cd4828f..53a5192 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -1350,6 +1350,7 @@
 
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/fonts.shard.test-cml",
+      "//build/config/fuchsia/test/mark_vmo_executable.shard.test-cml",
       "//build/config/fuchsia/test/present_view.shard.test-cml",
       "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
     ]
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 4cea511..0fa34b6fc 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -173,7 +173,6 @@
   "cr_elements/cr_radio_button/cr_radio_button_behavior.d.ts",
   "cr_elements/cr_radio_button/cr_radio_button.d.ts",
   "cr_elements/cr_scrollable_behavior.m.d.ts",
-  "cr_elements/cr_toggle/cr_toggle.d.ts",
   "cr_elements/find_shortcut_behavior.d.ts",
   "cr_elements/policy/cr_policy_indicator_behavior.m.d.ts",
   "cr_elements/policy/cr_policy_indicator.m.d.ts",
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_dialog.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_dialog.js
index 3ecd1a8..12acac30 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_dialog.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_dialog.js
@@ -10,7 +10,6 @@
  */
 
 import '../../../cr_elements/cr_button/cr_button.js';
-import '../../../cr_elements/cr_input/cr_input.js';
 import '../../../cr_elements/hidden_style_css.m.js';
 import '../../../js/cr.m.js';
 import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
@@ -19,6 +18,7 @@
 import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CrDialogElement} from '../../../cr_elements/cr_dialog/cr_dialog.js';
+import {CrInputElement} from '../../../cr_elements/cr_input/cr_input.js';
 import {assert} from '../../../js/assert.m.js';
 import {I18nBehavior} from '../../../js/i18n_behavior.m.js';
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
index 9d6b09f..82b9dbc 100644
--- a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
@@ -358,11 +358,12 @@
     "//third_party/polymer/v3_0/components-chromium/paper-spinner:paper-spinner-lite",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:load_time_data.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":network_config_module" ]
 }
 
@@ -406,8 +407,9 @@
     ":network_config_element_behavior.m",
     ":network_shared_css.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":network_config_toggle_module" ]
 }
 
@@ -430,9 +432,10 @@
     ":network_shared_css.m",
     ":onc_mojo.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/cr_elements/policy:cr_policy_indicator.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":network_ip_config_module" ]
 }
 
@@ -536,10 +539,11 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_button:cr_button",
     "//ui/webui/resources/cr_elements/cr_input:cr_input",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":network_proxy_module" ]
 }
 
@@ -601,11 +605,12 @@
     "//third_party/polymer/v3_0/components-chromium/iron-icon:iron-icon",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_button:cr_button",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior",
     "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":network_siminfo_module" ]
 }
 
@@ -640,9 +645,10 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_button:cr_button",
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
-    "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
+  externs_list =
+      [ "//ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js" ]
   extra_deps = [ ":sim_lock_dialogs_module" ]
 }
 
@@ -754,7 +760,7 @@
   js_file = "network_ip_config.js"
   html_file = "network_ip_config.html"
   html_type = "dom-module"
-  auto_imports = cr_components_chromeos_auto_imports + [ "ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html|CrToggleElement" ]
+  auto_imports = cr_components_chromeos_auto_imports
   migrated_imports = cr_components_migrated_imports
 }
 
@@ -844,7 +850,7 @@
   js_file = "network_siminfo.js"
   html_file = "network_siminfo.html"
   html_type = "dom-module"
-  auto_imports = cr_components_chromeos_auto_imports + [ "ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html|CrToggleElement" ]
+  auto_imports = cr_components_chromeos_auto_imports
   migrated_imports = cr_components_migrated_imports
   namespace_rewrites = cr_components_chromeos_namespace_rewrites
 }
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn b/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
index 83d1b69..cdef400 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
@@ -64,7 +64,9 @@
   html_type = "dom-module"
   namespace_rewrites = cr_components_chromeos_namespace_rewrites
   migrated_imports = cr_components_migrated_imports
-  auto_imports = cr_components_chromeos_auto_imports
+  auto_imports =
+      cr_components_chromeos_auto_imports +
+      [ "ui/webui/resources/cr_elements/cr_input/cr_input.html|CrInputElement" ]
 }
 
 polymer_modulizer("setup_pin_keyboard") {
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn
index 599b95bf..53ad55c 100644
--- a/ui/webui/resources/cr_elements/BUILD.gn
+++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -139,7 +139,6 @@
       "cr_radio_button/cr_card_radio_button.js",
       "cr_radio_button/cr_radio_button.js",
       "cr_scrollable_behavior.m.js",
-      "cr_toggle/cr_toggle.js",
       "hidden_style_css.m.js",
       "icons.m.js",
       "md_select_css.m.js",
@@ -175,7 +174,6 @@
       "cr_input:closure_compile_module",
       "cr_lottie:closure_compile_module",
       "cr_radio_button:closure_compile_module",
-      "cr_toggle:closure_compile_module",
       "policy:closure_compile_module",
     ]
   }
@@ -274,7 +272,6 @@
       "cr_input:web_components",
       "cr_lottie:cr_lottie_module",
       "cr_radio_button:web_components",
-      "cr_toggle:web_components",
       "policy:polymer3_elements",
     ]
 
diff --git a/ui/webui/resources/cr_elements/cr_elements.gni b/ui/webui/resources/cr_elements/cr_elements.gni
index 263e74b8..4c3b17f 100644
--- a/ui/webui/resources/cr_elements/cr_elements.gni
+++ b/ui/webui/resources/cr_elements/cr_elements.gni
@@ -44,6 +44,7 @@
     "cr_tabs/cr_tabs.ts",
     "cr_toast/cr_toast_manager.ts",
     "cr_toast/cr_toast.ts",
+    "cr_toggle/cr_toggle.ts",
     "cr_toolbar/cr_toolbar.ts",
     "cr_toolbar/cr_toolbar_search_field.ts",
     "cr_toolbar/cr_toolbar_selection_overlay.ts",
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.d.ts b/ui/webui/resources/cr_elements/cr_input/cr_input.d.ts
index eeca058..e0e062b 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input.d.ts
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input.d.ts
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {LegacyElementMixin} from 'chrome://resources/polymer/v3_0/polymer/lib/legacy/legacy-element-mixin.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-interface CrInputElement extends LegacyElementMixin, HTMLElement {
+interface CrInputElement extends PolymerElement {
   ariaDescription: string|undefined;
   autofocus: boolean;
   autoValidate: boolean|null|undefined;
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.js b/ui/webui/resources/cr_elements/cr_input/cr_input.js
index 78fff96..3f8e0bc 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input.js
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input.js
@@ -8,7 +8,7 @@
 import '../shared_vars_css.m.js';
 import './cr_input_style.css.js';
 
-import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {assert} from '../../js/assert.m.js';
 
@@ -57,141 +57,150 @@
  *     <cr-button slot="suffix"></cr-button>
  *   </cr-input>
  */
-Polymer({
-  is: 'cr-input',
+export class CrInputElement extends PolymerElement {
+  static get is() {
+    return 'cr-input';
+  }
 
-  _template: html`{__html_template__}`,
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-  properties: {
-    /** @type {string|undefined} */
-    ariaDescription: {
-      type: String,
-    },
+  static get properties() {
+    return {
+      /** @type {string|undefined} */
+      ariaDescription: {
+        type: String,
+      },
 
-    ariaLabel: {
-      type: String,
-      value: '',
-    },
+      ariaLabel: {
+        type: String,
+        value: '',
+      },
 
-    autofocus: {
-      type: Boolean,
-      value: false,
-      reflectToAttribute: true,
-    },
+      autofocus: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
 
-    autoValidate: Boolean,
+      autoValidate: Boolean,
 
-    disabled: {
-      type: Boolean,
-      value: false,
-      reflectToAttribute: true,
-    },
+      disabled: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
 
-    errorMessage: {
-      type: String,
-      value: '',
-      observer: 'onInvalidOrErrorMessageChanged_',
-    },
+      errorMessage: {
+        type: String,
+        value: '',
+        observer: 'onInvalidOrErrorMessageChanged_',
+      },
 
-    /** @private */
-    displayErrorMessage_: {
-      type: String,
-      value: '',
-    },
+      /** @private */
+      displayErrorMessage_: {
+        type: String,
+        value: '',
+      },
 
-    /**
-     * This is strictly used internally for styling, do not attempt to use
-     * this to set focus.
-     * @private
-     */
-    focused_: {
-      type: Boolean,
-      value: false,
-      reflectToAttribute: true,
-    },
+      /**
+       * This is strictly used internally for styling, do not attempt to use
+       * this to set focus.
+       * @private
+       */
+      focused_: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
 
-    invalid: {
-      type: Boolean,
-      value: false,
-      notify: true,
-      reflectToAttribute: true,
-      observer: 'onInvalidOrErrorMessageChanged_',
-    },
+      invalid: {
+        type: Boolean,
+        value: false,
+        notify: true,
+        reflectToAttribute: true,
+        observer: 'onInvalidOrErrorMessageChanged_',
+      },
 
-    max: {
-      type: Number,
-      reflectToAttribute: true,
-    },
+      max: {
+        type: Number,
+        reflectToAttribute: true,
+      },
 
-    min: {
-      type: Number,
-      reflectToAttribute: true,
-    },
+      min: {
+        type: Number,
+        reflectToAttribute: true,
+      },
 
-    maxlength: {
-      type: Number,
-      reflectToAttribute: true,
-    },
+      maxlength: {
+        type: Number,
+        reflectToAttribute: true,
+      },
 
-    minlength: {
-      type: Number,
-      reflectToAttribute: true,
-    },
+      minlength: {
+        type: Number,
+        reflectToAttribute: true,
+      },
 
-    pattern: {
-      type: String,
-      reflectToAttribute: true,
-    },
+      pattern: {
+        type: String,
+        reflectToAttribute: true,
+      },
 
-    inputmode: String,
+      inputmode: String,
 
-    label: {
-      type: String,
-      value: '',
-    },
+      label: {
+        type: String,
+        value: '',
+      },
 
-    /** @type {?string} */
-    placeholder: {
-      type: String,
-      value: null,
-      observer: 'placeholderChanged_',
-    },
+      /** @type {?string} */
+      placeholder: {
+        type: String,
+        value: null,
+        observer: 'placeholderChanged_',
+      },
 
-    readonly: {
-      type: Boolean,
-      reflectToAttribute: true,
-    },
+      readonly: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
 
-    required: {
-      type: Boolean,
-      reflectToAttribute: true,
-    },
+      required: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
 
-    /** @type {?number} */
-    inputTabindex: {
-      type: Number,
-      value: 0,
-      observer: 'onInputTabindexChanged_',
-    },
+      /** @type {?number} */
+      inputTabindex: {
+        type: Number,
+        value: 0,
+        observer: 'onInputTabindexChanged_',
+      },
 
-    type: {
-      type: String,
-      value: 'text',
-      observer: 'onTypeChanged_',
-    },
+      type: {
+        type: String,
+        value: 'text',
+        observer: 'onTypeChanged_',
+      },
 
-    value: {
-      type: String,
-      value: '',
-      notify: true,
-      observer: 'onValueChanged_',
-    },
-  },
+      value: {
+        type: String,
+        value: '',
+        notify: true,
+        observer: 'onValueChanged_',
+      },
+    };
+  }
 
+  /** @override */
   ready() {
+    super.ready();
+
     // Use inputTabindex instead.
     assert(!this.hasAttribute('tabindex'));
-  },
+  }
 
   /** @private */
   onInputTabindexChanged_() {
@@ -199,18 +208,18 @@
     // having the input in tab order or not. Values greater than 0 will not work
     // as the shadow root encapsulates tabindices.
     assert(this.inputTabindex === 0 || this.inputTabindex === -1);
-  },
+  }
 
   /** @private */
   onTypeChanged_() {
     // Check that the 'type' is one of the supported types.
     assert(SUPPORTED_INPUT_TYPES.has(this.type));
-  },
+  }
 
   /** @return {!HTMLInputElement} */
   get inputElement() {
     return /** @type {!HTMLInputElement} */ (this.$.input);
-  },
+  }
 
   /**
    * Returns the aria label to be used with the input element.
@@ -219,7 +228,7 @@
    */
   getAriaLabel_(ariaLabel, label, placeholder) {
     return ariaLabel || label || placeholder;
-  },
+  }
 
   /**
    * Returns 'true' or 'false' as a string for the aria-invalid attribute.
@@ -228,7 +237,7 @@
    */
   getAriaInvalid_(invalid) {
     return invalid ? 'true' : 'false';
-  },
+  }
 
   /** @private */
   onInvalidOrErrorMessageChanged_() {
@@ -238,7 +247,7 @@
     // changes. Adding and removing the |role| attribute every time there
     // is an error, triggers VoiceOver to consistently announce.
     const ERROR_ID = 'error';
-    const errorElement = this.$$(`#${ERROR_ID}`);
+    const errorElement = this.shadowRoot.querySelector(`#${ERROR_ID}`);
     if (this.invalid) {
       errorElement.setAttribute('role', 'alert');
       this.inputElement.setAttribute('aria-errormessage', ERROR_ID);
@@ -246,7 +255,7 @@
       errorElement.removeAttribute('role');
       this.inputElement.removeAttribute('aria-errormessage');
     }
-  },
+  }
 
   /**
    * This is necessary instead of doing <input placeholder="[[placeholder]]">
@@ -260,11 +269,11 @@
     } else {
       this.inputElement.removeAttribute('placeholder');
     }
-  },
+  }
 
   focus() {
     this.focusInput();
-  },
+  }
 
   /**
    * Focuses the input element.
@@ -278,7 +287,7 @@
     }
     this.inputElement.focus();
     return true;
-  },
+  }
 
   /**
    * @param {string} newValue
@@ -292,7 +301,7 @@
     if (this.autoValidate) {
       this.validate();
     }
-  },
+  }
 
   /**
    * 'change' event fires when <input> value changes and user presses 'Enter'.
@@ -302,18 +311,19 @@
    * @private
    */
   onInputChange_(e) {
-    this.fire('change', {sourceEvent: e});
-  },
+    this.dispatchEvent(new CustomEvent(
+        'change', {bubbles: true, composed: true, detail: {sourceEvent: e}}));
+  }
 
   /** @private */
   onInputFocus_() {
     this.focused_ = true;
-  },
+  }
 
   /** @private */
   onInputBlur_() {
     this.focused_ = false;
-  },
+  }
 
   /**
    * Selects the text within the input. If no parameters are passed, it will
@@ -333,11 +343,13 @@
       assert(start === undefined && end === undefined);
       this.inputElement.select();
     }
-  },
+  }
 
   /** @return {boolean} */
   validate() {
     this.invalid = !this.inputElement.checkValidity();
     return !this.invalid;
-  },
-});
+  }
+}
+
+customElements.define(CrInputElement.is, CrInputElement);
diff --git a/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn b/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn
deleted file mode 100644
index 1edd072f..0000000
--- a/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-import("//tools/polymer/html_to_js.gni")
-
-html_to_js("web_components") {
-  js_files = [ "cr_toggle.js" ]
-}
-
-js_type_check("closure_compile_module") {
-  is_polymer3 = true
-  deps = [ ":cr_toggle" ]
-}
-
-js_library("cr_toggle") {
-  deps = [
-    "//third_party/polymer/v3_0/components-chromium/paper-behaviors:paper-ripple-behavior",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-}
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.d.ts b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.d.ts
deleted file mode 100644
index 18cd9222..0000000
--- a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.d.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {LegacyElementMixin} from 'chrome://resources/polymer/v3_0/polymer/lib/legacy/legacy-element-mixin.js';
-
-export const MOVE_THRESHOLD_PX: number;
-
-interface CrToggleElement extends LegacyElementMixin, HTMLElement {
-  checked: boolean;
-  dark: boolean;
-  disabled: boolean;
-}
-
-export {CrToggleElement};
-
-declare global {
-  interface HTMLElementTagNameMap {
-    'cr-toggle': CrToggleElement;
-  }
-}
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.ts
similarity index 70%
rename from ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
rename to ui/webui/resources/cr_elements/cr_toggle/cr_toggle.ts
index d362cd60..8e7eba7e 100644
--- a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
+++ b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.ts
@@ -5,9 +5,8 @@
 /**
  * Number of pixels required to move to consider the pointermove event as
  * intentional.
- * @type {number}
  */
-export const MOVE_THRESHOLD_PX = 5;
+export const MOVE_THRESHOLD_PX: number = 5;
 
 /**
  * @fileoverview 'cr-toggle' is a component for showing an on/off switch. It
@@ -16,30 +15,28 @@
  * dragging (pointerdown+pointermove) the element towards the desired direction.
  */
 import {PaperRippleBehavior} from '//resources/polymer/v3_0/paper-behaviors/paper-ripple-behavior.js';
-import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assert} from '//resources/js/assert_ts.js';
 import '../shared_vars_css.m.js';
+import {getTemplate} from './cr_toggle.html.js';
 
-class PaperRippleBehaviorInterface {
-  /** @return {!PaperRippleElement} */
-  getRipple() {}
+const CrToggleElementBase =
+    mixinBehaviors([PaperRippleBehavior], PolymerElement) as
+    {new (): PolymerElement & PaperRippleBehavior};
+
+export interface CrToggleElement {
+  $: {
+    knob: HTMLElement,
+  };
 }
 
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {PaperRippleBehaviorInterface}
- */
-const CrToggleElementBase =
-    mixinBehaviors([PaperRippleBehavior], PolymerElement);
-
-/** @polymer */
 export class CrToggleElement extends CrToggleElementBase {
   static get is() {
     return 'cr-toggle';
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
@@ -67,27 +64,22 @@
     };
   }
 
-  constructor() {
-    super();
-    /** @private {?Function} */
-    this.boundPointerMove_ = null;
+  checked: boolean;
+  dark: boolean;
+  disabled: boolean;
 
-    /**
-     * Whether the state of the toggle has already taken into account by
-     * |pointeremove| handlers. Used in the 'click' handler.
-     * @private {boolean}
-     */
-    this.handledInPointerMove_ = false;
+  private boundPointerMove_: ((e: PointerEvent) => void)|null = null;
+  /**
+   * Whether the state of the toggle has already taken into account by
+   * |pointeremove| handlers. Used in the 'click' handler.
+   */
+  private handledInPointerMove_: boolean = false;
+  private pointerDownX_: number = 0;
 
-    /** @private {?number} */
-    this.pointerDownX_ = null;
+  /* eslint-disable-next-line @typescript-eslint/naming-convention */
+  override _rippleContainer: Element;
 
-    /** @type {!Element} */
-    this._rippleContainer;
-  }
-
-  /** @override */
-  ready() {
+  override ready() {
     super.ready();
     if (!this.hasAttribute('role')) {
       this.setAttribute('role', 'button');
@@ -99,24 +91,19 @@
     this.setAttribute('aria-disabled', 'false');
   }
 
-  /** @override */
-  connectedCallback() {
+  override connectedCallback() {
     super.connectedCallback();
     this.addEventListener('blur', this.hideRipple_.bind(this));
     this.addEventListener('click', this.onClick_.bind(this));
     this.addEventListener('focus', this.onFocus_.bind(this));
-    this.addEventListener(
-        'keydown', e => this.onKeyDown_(/** @type {!KeyboardEvent} */ (e)));
-    this.addEventListener(
-        'keyup', e => this.onKeyUp_(/** @type {!KeyboardEvent} */ (e)));
-    this.addEventListener(
-        'pointerdown',
-        e => this.onPointerDown_(/** @type {!PointerEvent} */ (e)));
+    this.addEventListener('keydown', this.onKeyDown_.bind(this));
+    this.addEventListener('keyup', this.onKeyUp_.bind(this));
+    this.addEventListener('pointerdown', this.onPointerDown_.bind(this));
     this.addEventListener('pointerup', this.onPointerUp_.bind(this));
 
     const direction =
         this.matches(':host-context([dir=rtl]) cr-toggle') ? -1 : 1;
-    this.boundPointerMove_ = (e) => {
+    this.boundPointerMove_ = (e: PointerEvent) => {
       // Prevent unwanted text selection to occur while moving the pointer, this
       // is important.
       e.preventDefault();
@@ -136,38 +123,30 @@
     };
   }
 
-  /** @private */
-  checkedChanged_() {
+  private checkedChanged_() {
     this.setAttribute('aria-pressed', this.checked ? 'true' : 'false');
   }
 
-  /** @private */
-  disabledChanged_() {
-    this.setAttribute('tabindex', this.disabled ? -1 : 0);
+  private disabledChanged_() {
+    this.setAttribute('tabindex', this.disabled ? '-1' : '0');
     this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
   }
 
-  /** @private */
-  onFocus_() {
+  private onFocus_() {
     this.getRipple().showAndHoldDown();
   }
 
-  /** @private */
-  hideRipple_() {
+  private hideRipple_() {
     this.getRipple().clear();
   }
 
-  /** @private */
-  onPointerUp_() {
+  private onPointerUp_() {
+    assert(this.boundPointerMove_);
     this.removeEventListener('pointermove', this.boundPointerMove_);
     this.hideRipple_();
   }
 
-  /**
-   * @param {!PointerEvent} e
-   * @private
-   */
-  onPointerDown_(e) {
+  private onPointerDown_(e: PointerEvent) {
     // Don't do anything if this was not a primary button click or touch event.
     if (e.button !== 0) {
       return;
@@ -178,14 +157,11 @@
     this.setPointerCapture(e.pointerId);
     this.pointerDownX_ = e.clientX;
     this.handledInPointerMove_ = false;
+    assert(this.boundPointerMove_);
     this.addEventListener('pointermove', this.boundPointerMove_);
   }
 
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onClick_(e) {
+  private onClick_(e: Event) {
     // Prevent |click| event from bubbling. It can cause parents of this
     // elements to erroneously re-toggle this control.
     e.stopPropagation();
@@ -202,11 +178,7 @@
     this.toggleState_(/* fromKeyboard= */ false);
   }
 
-  /**
-   * @param {boolean} fromKeyboard
-   * @private
-   */
-  toggleState_(fromKeyboard) {
+  private toggleState_(fromKeyboard: boolean) {
     // Ignore cases where the 'click' or 'keypress' handlers are triggered while
     // disabled.
     if (this.disabled) {
@@ -222,11 +194,7 @@
         'change', {bubbles: true, composed: true, detail: this.checked}));
   }
 
-  /**
-   * @param {!KeyboardEvent} e
-   * @private
-   */
-  onKeyDown_(e) {
+  private onKeyDown_(e: KeyboardEvent) {
     if (e.key !== ' ' && e.key !== 'Enter') {
       return;
     }
@@ -242,11 +210,7 @@
     }
   }
 
-  /**
-   * @param {!KeyboardEvent} e
-   * @private
-   */
-  onKeyUp_(e) {
+  private onKeyUp_(e: KeyboardEvent) {
     if (e.key !== ' ' && e.key !== 'Enter') {
       return;
     }
@@ -259,10 +223,11 @@
     }
   }
 
-  // customize the element's ripple
-  _createRipple() {
+  // Overridden from PaperRippleBehavior
+  /* eslint-disable-next-line @typescript-eslint/naming-convention */
+  override _createRipple() {
     this._rippleContainer = this.$.knob;
-    const ripple = PaperRippleBehavior._createRipple();
+    const ripple = super._createRipple();
     ripple.id = 'ink';
     ripple.setAttribute('recenters', '');
     ripple.classList.add('circle', 'toggle-ink');
@@ -270,4 +235,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'cr-toggle': CrToggleElement;
+  }
+}
+
 customElements.define(CrToggleElement.is, CrToggleElement);
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js
new file mode 100644
index 0000000..dc2e8b44
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle_externs.js
@@ -0,0 +1,14 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* Minimal externs file provided for places in the code that
+ * still use JavaScript instead of TypeScript.
+ * @externs
+ */
+
+/**
+ * @constructor
+ * @extends {HTMLElement}
+ */
+function CrToggleElement() {}
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index a96e790..690654a0 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -18,6 +18,7 @@
       h1 {
         flex: 1;
         font-size: 170%;
+        white-space: var(--cr-toolbar-header-white-space, normal);
         font-weight: var(--cr-toolbar-header-font-weight, 500);
         letter-spacing: .25px;
         line-height: normal;