diff --git a/.gn b/.gn
index ddf99d1d..9dc1fd1 100644
--- a/.gn
+++ b/.gn
@@ -66,6 +66,7 @@
   "//extensions:*",  # 28 errors
   "//headless:*",  # 107 errors
   "//ppapi/proxy:ipc_sources",  # 13 errors
+  "//remoting/host/security_key:*",  # 10 errors
 
   "//third_party/icu/*",
   "//third_party/libwebp:*",  # 7 errors, https://crbug.com/800762
diff --git a/DEPS b/DEPS
index a0ff6d8d..0273f608 100644
--- a/DEPS
+++ b/DEPS
@@ -213,7 +213,7 @@
   'dawn_standalone': False,
 
   # reclient CIPD package version
-  'reclient_version': 're_client_version:0.37.0.c6161fa',
+  'reclient_version': 're_client_version:0.38.0.5f131f2-gomaip',
 
   'android_git': 'https://android.googlesource.com',
   'aomedia_git': 'https://aomedia.googlesource.com',
@@ -228,11 +228,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'eb342e13d0ed4cdba262bbc8533fb271510a96fd',
+  'skia_revision': 'b5de6be2a85db643be308096ee30cd70fa830ca0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '76d48acb02956cbee98234f32e7167a7750a0497',
+  'v8_revision': 'f9a433890547bc1b65b837a8ffb96150a6cf3c6e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -240,7 +240,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': 'd25c9d7f5bb02ec22e6b9f519e69dc84c4bb2150',
+  'angle_revision': '8f214193940b2e7eda70746b7b42f96b0b4ce73b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -299,7 +299,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b35ee4986edcfc8938f649d20906b4adb98a3cfe',
+  'catapult_revision': 'abc7ba7d871fe3c25b0a1bec7fc84fb309034cb7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -347,7 +347,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': 'ebbbcc7ecb1cbbaf4c5c12b701c650244dfc7e20',
+  'dawn_revision': '5d17ed6541afd44ffcc2e267d533ebd03354c75d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -403,11 +403,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libjxl_revision': '4a981fd8be383703ca8c5dc78c25411c14a01d9f',
+  'libjxl_revision': 'c4e0877f93506e880cd922f6c94644d79ae9adff',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'highway_revision': 'ca1a57c342cd815053abfcffa29b44eaead4f20b',
+  'highway_revision': '424360251cdcfc314cfc528f53c872ecd63af0f0',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
@@ -550,7 +550,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/enterprise/connectors/file_system/captured_sites',
-        'version': 'pthwS0AAeS_iCyAZGv69ts0CMdoRfU_3DhdKbghyExwC',
+        'version': 'kJXdHLymr3WKkC6vLmnQXgBtrAa6i33T63g3hcxUGoIC',
       }
     ],
     'condition': 'checkout_chromium_fsc_test_dependencies',
@@ -610,7 +610,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c45ee22cb4f6f56a7afca1e1ce66942e77672a65',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'b156c37d0faa70c2dc8d137f198dc28f174cf237',
       'condition': 'checkout_ios',
   },
 
@@ -767,7 +767,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '2x0RaVwBwxRoEqsOfKvBqGyoI_asDh5FLRC5F_o9PiYC',
+          'version': '5FmojmYp53y0XBXcZuz3Mglv3JiYPGYex2LMT6kbzv8C',
       },
     ],
     'condition': 'checkout_android',
@@ -1003,7 +1003,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4f3583d6d585894cc99481e7ab850c65e864f968',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '49a703f3d915b140c9f373107e1ba17f30e2487d',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1682,7 +1682,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0e8a492cf58f73346ac71ef2fceb7a94580f9b32',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f416b8e9038f7ca88ddd2c83e37f892acff4005c',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 155708e..8ef9e66d 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2583,7 +2583,6 @@
     "//ui/aura:test_support",
     "//ui/base",
     "//ui/base:test_support",
-    "//ui/base/cursor",
     "//ui/base/cursor:cursor_base",
     "//ui/base/cursor/mojom:cursor_type",
     "//ui/base/dragdrop:types",
@@ -2607,7 +2606,6 @@
     "//ui/events/devices",
     "//ui/events/devices:test_support",
     "//ui/gfx",
-    "//ui/gfx:geometry_skia",
     "//ui/gfx:test_support",
     "//ui/gfx/geometry",
     "//ui/gl:test_support",
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index e531a72..20b727d 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -1081,6 +1081,208 @@
   EXPECT_EQ("Item 2", apps_grid_view_->GetItemViewAt(2)->item()->id());
 }
 
+// Tests that app list item drag gets canceled if the dragged app list item gets
+// deleted.
+TEST_P(PopulatedAppListTest, CancelItemDragOnDragItemDeletion) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(4);
+
+  // Start dragging a view.
+  AppListItemView* const dragged_view = apps_grid_view_->GetItemViewAt(0);
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->MoveMouseTo(dragged_view->GetBoundsInScreen().CenterPoint());
+  event_generator->PressLeftButton();
+  dragged_view->FireMouseDragTimerForTest();
+  event_generator->MoveMouseTo(
+      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().left_center());
+  EXPECT_TRUE(apps_grid_view_->IsDragging());
+
+  // Delete the dragged item.
+  app_list_test_model_->DeleteUninstalledItem(dragged_view->item()->id());
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+
+  // Verify that mouse drag has been canceled.
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+
+  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(0)->item()->id());
+  EXPECT_EQ("Item 2", apps_grid_view_->GetItemViewAt(1)->item()->id());
+  EXPECT_EQ("Item 3", apps_grid_view_->GetItemViewAt(2)->item()->id());
+
+  // Hide and show the app list again to verify checks done when resetting the
+  // apps grid for show pass (e.g. verification that size of the app list views
+  // model matches the size of app list data model).
+  AppListTestHelper* helper = GetAppListTestHelper();
+  helper->ShowAndRunLoop(GetPrimaryDisplay().id());
+  helper->DismissAndRunLoop();
+}
+
+// Tests that app list item drag in folder gets canceled if the dragged app list
+// item gets deleted.
+TEST_P(PopulatedAppListTest, CancelFolderItemDragOnDragItemDeletion) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(2);
+  AppListFolderItem* folder =
+      app_list_test_model_->CreateAndPopulateFolderWithApps(3);
+  app_list_test_model_->PopulateApps(3);
+
+  // Tap the folder item to show it.
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->GestureTapAt(
+      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().CenterPoint());
+  ASSERT_TRUE(AppListIsInFolderView());
+
+  // Start dragging the first item in the active folder.
+  AppListItemView* const dragged_view =
+      folder_view()->items_grid_view()->GetItemViewAt(0);
+  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
+  event_generator->PressTouch();
+  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
+  event_generator->MoveTouchBy(10, 10);
+
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+  EXPECT_TRUE(folder_view()->items_grid_view()->IsDragging());
+
+  // Delete the dragged item.
+  app_list_test_model_->DeleteUninstalledItem(dragged_view->item()->id());
+
+  // Verify that drag has been canceled.
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+  EXPECT_FALSE(folder_view()->items_grid_view()->IsDragging());
+
+  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(0)->item()->id());
+  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(1)->item()->id());
+  EXPECT_EQ(folder->id(), apps_grid_view_->GetItemViewAt(2)->item()->id());
+  EXPECT_EQ("Item 3",
+            folder_view()->items_grid_view()->GetItemViewAt(0)->item()->id());
+
+  // Hide and show the app list again to verify checks done when resetting the
+  // apps grid for show pass (e.g. verification that size of the app list views
+  // model matches the size of app list data model).
+  AppListTestHelper* helper = GetAppListTestHelper();
+  helper->ShowAndRunLoop(GetPrimaryDisplay().id());
+  helper->DismissAndRunLoop();
+}
+
+// Tests that app list item drag from folder to root apps grid gets canceled if
+// the dragged app list item gets deleted.
+TEST_P(PopulatedAppListTest, CancelFolderItemReparentDragOnDragItemDeletion) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(2);
+  AppListFolderItem* folder =
+      app_list_test_model_->CreateAndPopulateFolderWithApps(3);
+  app_list_test_model_->PopulateApps(3);
+
+  // Tap the folder item to show it.
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->GestureTapAt(
+      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().CenterPoint());
+  ASSERT_TRUE(AppListIsInFolderView());
+
+  // Start dragging the first item in the active folder.
+  AppListItemView* const dragged_view =
+      folder_view()->items_grid_view()->GetItemViewAt(0);
+  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
+  event_generator->PressTouch();
+  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
+  event_generator->MoveTouchBy(10, 10);
+
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+  EXPECT_TRUE(folder_view()->items_grid_view()->IsDragging());
+
+  // Drag the item outside the folder bounds.
+  event_generator->MoveTouch(
+      apps_grid_view_->GetItemViewAt(1)->GetBoundsInScreen().CenterPoint());
+  event_generator->MoveTouchBy(2, 2);
+
+  // Fire reparenting timer.
+  EXPECT_TRUE(
+      folder_view()->items_grid_view()->FireFolderItemReparentTimerForTest());
+  EXPECT_FALSE(AppListIsInFolderView());
+  event_generator->MoveTouch(
+      apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen().CenterPoint());
+
+  EXPECT_TRUE(apps_grid_view_->IsDragging());
+  EXPECT_TRUE(folder_view()->items_grid_view()->IsDragging());
+
+  // Delete the dragged item.
+  app_list_test_model_->DeleteUninstalledItem(dragged_view->item()->id());
+
+  // Verify that drag has been canceled.
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+  EXPECT_FALSE(folder_view()->items_grid_view()->IsDragging());
+
+  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(0)->item()->id());
+  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(1)->item()->id());
+  EXPECT_EQ(folder->id(), apps_grid_view_->GetItemViewAt(2)->item()->id());
+  EXPECT_EQ("Item 5", apps_grid_view_->GetItemViewAt(3)->item()->id());
+
+  // Hide and show the app list again to verify checks done when resetting the
+  // apps grid for show pass (e.g. verification that size of the app list views
+  // model matches the size of app list data model).
+  AppListTestHelper* helper = GetAppListTestHelper();
+  helper->ShowAndRunLoop(GetPrimaryDisplay().id());
+  helper->DismissAndRunLoop();
+}
+
+TEST_P(PopulatedAppListTest,
+       CancelFolderItemReparentDragOnDragItemAndFolderDeletion) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(2);
+  app_list_test_model_->CreateAndPopulateFolderWithApps(2);
+  app_list_test_model_->PopulateApps(3);
+
+  // Tap the folder item to show it.
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->GestureTapAt(
+      apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen().CenterPoint());
+  ASSERT_TRUE(AppListIsInFolderView());
+
+  // Start dragging the first item in the active folder.
+  AppListItemView* const dragged_view =
+      folder_view()->items_grid_view()->GetItemViewAt(0);
+  event_generator->MoveTouch(dragged_view->GetBoundsInScreen().CenterPoint());
+  event_generator->PressTouch();
+  ASSERT_TRUE(dragged_view->FireTouchDragTimerForTest());
+  event_generator->MoveTouchBy(10, 10);
+
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+  EXPECT_TRUE(folder_view()->items_grid_view()->IsDragging());
+
+  // Drag the item outside the folder bounds.
+  event_generator->MoveTouch(
+      apps_grid_view_->GetItemViewAt(1)->GetBoundsInScreen().CenterPoint());
+  event_generator->MoveTouchBy(2, 2);
+
+  // Fire reparenting timer.
+  EXPECT_TRUE(
+      folder_view()->items_grid_view()->FireFolderItemReparentTimerForTest());
+  EXPECT_FALSE(AppListIsInFolderView());
+  event_generator->MoveTouch(
+      apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen().CenterPoint());
+
+  EXPECT_TRUE(apps_grid_view_->IsDragging());
+  EXPECT_TRUE(folder_view()->items_grid_view()->IsDragging());
+
+  // Delete the dragged item.
+  app_list_test_model_->DeleteUninstalledItem(dragged_view->item()->id());
+
+  // Verify that drag has been canceled.
+  EXPECT_FALSE(apps_grid_view_->IsDragging());
+  EXPECT_FALSE(folder_view()->items_grid_view()->IsDragging());
+
+  EXPECT_EQ("Item 0", apps_grid_view_->GetItemViewAt(0)->item()->id());
+  EXPECT_EQ("Item 1", apps_grid_view_->GetItemViewAt(1)->item()->id());
+  EXPECT_EQ("Item 3", apps_grid_view_->GetItemViewAt(2)->item()->id());
+  EXPECT_EQ("Item 4", apps_grid_view_->GetItemViewAt(3)->item()->id());
+
+  // Hide and show the app list again to verify checks done when resetting the
+  // apps grid for show pass (e.g. verification that size of the app list views
+  // model matches the size of app list data model).
+  AppListTestHelper* helper = GetAppListTestHelper();
+  helper->ShowAndRunLoop(GetPrimaryDisplay().id());
+  helper->DismissAndRunLoop();
+}
+
 // Tests that apps grid item layers are not destroyed immediately after item
 // drag ends.
 TEST_P(PopulatedAppListTest,
diff --git a/ash/app_list/model/app_list_item_list.cc b/ash/app_list/model/app_list_item_list.cc
index a5e6df4..f8aadaa 100644
--- a/ash/app_list/model/app_list_item_list.cc
+++ b/ash/app_list/model/app_list_item_list.cc
@@ -178,6 +178,8 @@
            << item_count() << " add index " << index;
   app_list_items_.insert(app_list_items_.begin() + index,
                          std::move(page_break_item));
+  for (auto& observer : observers_)
+    observer.OnListItemAdded(index, item);
   return item;
 }
 
diff --git a/ash/app_list/paged_view_structure.cc b/ash/app_list/paged_view_structure.cc
index 43b22ec..9dbb8c63 100644
--- a/ash/app_list/paged_view_structure.cc
+++ b/ash/app_list/paged_view_structure.cc
@@ -303,7 +303,7 @@
 }
 
 int PagedViewStructure::GetTargetModelIndexForMove(
-    AppListItemView* moved_view,
+    AppListItem* moved_item,
     const GridIndex& index) const {
   if (mode_ == Mode::kSinglePage || mode_ == Mode::kFullPages)
     return GetModelIndexFromIndex(index);
@@ -317,8 +317,12 @@
     // Skip the item view to be moved in the page if found.
     // Decrement |target_model_index| if |moved_view| is in this page because it
     // is represented by a placeholder.
-    auto iter = std::find(page.begin(), page.end(), moved_view);
-    if (iter != page.end())
+    auto it =
+        std::find_if(page.begin(), page.end(), [&](AppListItemView* item_view) {
+          return item_view->item() == moved_item;
+        });
+
+    if (it != page.end())
       --target_model_index;
   }
 
@@ -329,7 +333,7 @@
 }
 
 int PagedViewStructure::GetTargetItemListIndexForMove(
-    AppListItemView* moved_view,
+    AppListItem* moved_item,
     const GridIndex& index) const {
   if (mode_ == Mode::kFullPages)
     return GetModelIndexFromIndex(index);
@@ -371,7 +375,7 @@
     while (current_item_index < item_list->item_count() &&
            !item_list->item_at(current_item_index)->is_page_break() &&
            current_index != index) {
-      if (moved_view->item() == item_list->item_at(current_item_index) &&
+      if (moved_item && moved_item == item_list->item_at(current_item_index) &&
           current_index.page < index.page) {
         // If the item view is moved to a following page, we need to skip the
         // item view. If the view is moved to the same page, do not skip the
diff --git a/ash/app_list/paged_view_structure.h b/ash/app_list/paged_view_structure.h
index 1d69505..b3f0426 100644
--- a/ash/app_list/paged_view_structure.h
+++ b/ash/app_list/paged_view_structure.h
@@ -15,6 +15,7 @@
 namespace ash {
 
 class AppsGridView;
+class AppListItem;
 class AppListItemView;
 struct GridIndex;
 
@@ -102,12 +103,12 @@
 
   // Returns the target model index if moving the item view to specified target
   // visual index.
-  int GetTargetModelIndexForMove(AppListItemView* moved_view,
+  int GetTargetModelIndexForMove(AppListItem* moved_item,
                                  const GridIndex& index) const;
 
   // Returns the target `AppsGridView::item_list_` index if moving the item view
   // to specified target visual index.
-  int GetTargetItemListIndexForMove(AppListItemView* moved_view,
+  int GetTargetItemListIndexForMove(AppListItem* moved_item,
                                     const GridIndex& index) const;
 
   // Returns true if the visual index is valid position to which an item view
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index b971ad0..078d06dc 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -248,8 +248,7 @@
       const AppListItem* top_item =
           folder_view_->folder_item()->item_list()->item_at(i);
       if (top_item->GetIcon(folder_view_->GetAppListConfig().type()).isNull() ||
-          (folder_view_->items_grid_view()->drag_view() &&
-           top_item == folder_view_->items_grid_view()->drag_view()->item())) {
+          top_item == folder_view_->items_grid_view()->drag_item()) {
         // The item being dragged should be excluded.
         continue;
       }
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 264977e..d69c0e23 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -1096,6 +1096,10 @@
   DCHECK(item_weak_);
   item_weak_->RemoveObserver(this);
   item_weak_ = nullptr;
+
+  // `EndDrag()` may delete this.
+  if (drag_state_ != DragState::kNone)
+    grid_delegate_->EndDrag(/*cancel=*/true);
 }
 
 int AppListItemView::GetPreviewCircleRadius() const {
diff --git a/ash/app_list/views/app_list_main_view_unittest.cc b/ash/app_list/views/app_list_main_view_unittest.cc
index fe034c98..f34e90e 100644
--- a/ash/app_list/views/app_list_main_view_unittest.cc
+++ b/ash/app_list/views/app_list_main_view_unittest.cc
@@ -377,19 +377,19 @@
   AppListItemView* dragged = StartDragForReparent(0);
 
   // Now add an item to the model, not in any folder, e.g., as if by Sync.
-  EXPECT_TRUE(GetRootGridView()->has_dragged_view());
-  EXPECT_TRUE(GetFolderGridView()->has_dragged_view());
+  EXPECT_TRUE(GetRootGridView()->has_dragged_item());
+  EXPECT_TRUE(GetFolderGridView()->has_dragged_item());
   delegate_->GetTestModel()->CreateAndAddItem("Extra");
 
   // The drag operation should get canceled.
-  EXPECT_FALSE(GetRootGridView()->has_dragged_view());
-  EXPECT_FALSE(GetFolderGridView()->has_dragged_view());
+  EXPECT_FALSE(GetRootGridView()->has_dragged_item());
+  EXPECT_FALSE(GetFolderGridView()->has_dragged_item());
 
   // Additional mouse move operations should be ignored.
   gfx::Point point(1, 1);
   SimulateUpdateDrag(GetFolderGridView(), AppsGridView::MOUSE, dragged, point);
-  EXPECT_FALSE(GetRootGridView()->has_dragged_view());
-  EXPECT_FALSE(GetFolderGridView()->has_dragged_view());
+  EXPECT_FALSE(GetRootGridView()->has_dragged_item());
+  EXPECT_FALSE(GetFolderGridView()->has_dragged_item());
 }
 
 // Test that dragging an app out of a single item folder and reparenting it
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 27c066a..34ed200e 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -150,28 +150,6 @@
   DISALLOW_COPY_AND_ASSIGN(RowMoveAnimationDelegate);
 };
 
-// ItemRemoveAnimationDelegate is used to show animation for removing an item.
-// This happens when user drags an item into a folder. The dragged item will
-// be removed from the original list after it is dropped into the folder.
-class ItemRemoveAnimationDelegate : public views::AnimationDelegateViews {
- public:
-  explicit ItemRemoveAnimationDelegate(views::View* view)
-      : views::AnimationDelegateViews(view), view_(view) {}
-
-  ~ItemRemoveAnimationDelegate() override = default;
-
-  // views::AnimationDelegateViews:
-  void AnimationProgressed(const gfx::Animation* animation) override {
-    view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
-    view_->layer()->ScheduleDraw();
-  }
-
- private:
-  std::unique_ptr<views::View> view_;
-
-  DISALLOW_COPY_AND_ASSIGN(ItemRemoveAnimationDelegate);
-};
-
 bool IsOEMFolderItem(AppListItem* item) {
   return IsFolderItem(item) &&
          (static_cast<AppListFolderItem*>(item))->folder_type() ==
@@ -274,6 +252,28 @@
       this};
 };
 
+// Class used by AppsGridView to track whether app list model is being updated
+// by the AppsGridView (by setting `updating_model_`). While this is in scope,
+// app list model changes will not cancel in progress drag, and will delay
+// `view_structure_` sanitization until the app list model update finishes.
+class AppsGridView::ScopedModelUpdate {
+ public:
+  explicit ScopedModelUpdate(AppsGridView* apps_grid_view)
+      : apps_grid_view_(apps_grid_view) {
+    apps_grid_view_->updating_model_ = true;
+    view_structure_sanitize_lock_ =
+        apps_grid_view_->view_structure_.GetSanitizeLock();
+  }
+  ScopedModelUpdate(const ScopedModelUpdate&) = delete;
+  ScopedModelUpdate& operator=(const ScopedModelUpdate&) = delete;
+  ~ScopedModelUpdate() { apps_grid_view_->updating_model_ = false; }
+
+ private:
+  AppsGridView* const apps_grid_view_;
+  std::unique_ptr<PagedViewStructure::ScopedSanitizeLock>
+      view_structure_sanitize_lock_;
+};
+
 AppsGridView::AppsGridView(ContentsView* contents_view,
                            AppListA11yAnnouncer* a11y_announcer,
                            AppListViewDelegate* app_list_view_delegate,
@@ -312,8 +312,8 @@
   // Coming here |drag_view_| should already be canceled since otherwise the
   // drag would disappear after the app list got animated away and closed,
   // which would look odd.
-  DCHECK(!drag_view_);
-  if (drag_view_)
+  DCHECK(!drag_item_);
+  if (drag_item_)
     EndDrag(true);
 
   if (model_)
@@ -454,7 +454,7 @@
                                 base::OnceClosure drag_start_callback,
                                 base::OnceClosure drag_end_callback) {
   DCHECK(view);
-  if (drag_view_ || pulsing_blocks_model_.view_size())
+  if (drag_item_ || pulsing_blocks_model_.view_size())
     return false;
   DVLOG(1) << "Initiate drag";
 
@@ -470,6 +470,7 @@
   for (const auto& entry : view_model_.entries())
     static_cast<AppListItemView*>(entry.view)->EnsureLayer();
   drag_view_ = view;
+  drag_item_ = view->item();
 
   // Dragged view should have focus. This also fixed the issue
   // https://crbug.com/834682.
@@ -487,13 +488,10 @@
 void AppsGridView::TryStartDragAndDropHostDrag(Pointer pointer) {
   // Stopping the animation may have invalidated our drag view due to the
   // view hierarchy changing.
-  if (!drag_view_)
+  if (!drag_item_)
     return;
 
   drag_pointer_ = pointer;
-  // Move the view to the front so that it appears on top of other views.
-  items_container_->ReorderChildView(drag_view_, -1);
-  bounds_animator_->StopAnimatingView(drag_view_);
 
   if (!dragging_for_reparent_item_)
     StartDragAndDropHostDrag();
@@ -504,7 +502,7 @@
 
 bool AppsGridView::UpdateDragFromItem(bool is_touch,
                                       const ui::LocatedEvent& event) {
-  if (!drag_view_)
+  if (!drag_item_)
     return false;  // Drag canceled.
 
   gfx::Point drag_point_in_grid_view;
@@ -530,7 +528,7 @@
   if (folder_delegate_)
     UpdateDragStateInsideFolder(pointer, point);
 
-  if (!drag_view_)
+  if (!drag_item_)
     return;  // Drag canceled.
 
   gfx::Vector2d drag_vector(point - drag_start_grid_view_);
@@ -597,7 +595,7 @@
   DVLOG(1) << __func__;
 
   // EndDrag was called before if |drag_view_| is nullptr.
-  if (!drag_view_)
+  if (!drag_item_)
     return;
 
   // Coming here a drag and drop was in progress.
@@ -610,7 +608,7 @@
   // This is the folder view to drop an item into. Cache the |drag_view_|'s item
   // and its bounds for later use in folder dropping animation.
   AppListItemView* folder_item_view = nullptr;
-  AppListItem* drag_item = drag_view_->item();
+  AppListItem* drag_item = drag_item_;
 
   if (forward_events_to_drag_and_drop_host_) {
     DCHECK(!IsDraggingForReparentInRootLevelGridView());
@@ -623,11 +621,6 @@
       folder_delegate_->DispatchEndDragEventForReparent(
           true /* events_forwarded_to_drag_drop_host */,
           cancel /* cancel_drag */, std::move(drag_icon_proxy_));
-    } else {
-      // |drag_view_| is reordered when initiating the drag. In addition, the
-      // icon's location in AppsGridView does not alter after being dragged to
-      // Shelf. So recover the order when drag ends.
-      MoveItemInModel(drag_view_, drag_view_init_index_);
     }
   } else {
     if (IsDraggingForReparentInHiddenGridView()) {
@@ -653,16 +646,12 @@
       if (drop_target_region_ == ON_ITEM && DraggedItemCanEnterFolder() &&
           DropTargetIsValidFolder()) {
         MaybeCreateFolderDroppingAccessibilityEvent();
-        folder_item_view = MoveItemToFolder(drag_view_, drop_target_);
-        // If the view that the folder is replacing had a layer, ensure the new
-        // folder view has one too.
-        if (drag_view_ && drag_view_->layer())
-          folder_item_view->EnsureLayer();
+        folder_item_view = MoveItemToFolder(drag_item_, drop_target_);
         reparented_into_folder = true;
       } else if (IsValidReorderTargetIndex(drop_target_)) {
         // Ensure reorder event has already been announced by the end of drag.
         MaybeCreateDragReorderAccessibilityEvent();
-        MoveItemInModel(drag_view_, drop_target_);
+        MoveItemInModel(drag_item_, drop_target_);
         RecordAppMovingTypeMetrics(folder_delegate_ ? kReorderByDragInFolder
                                                     : kReorderByDragInTopLevel);
       }
@@ -686,13 +675,6 @@
 
   SetAsFolderDroppingTarget(drop_target_, false);
 
-  // Keep track of the |drag_view| after it is released to ensure that it does
-  // not have a visible title until its animation to ideal bounds is complete.
-  // Do not cache the drag view if it's reparented into folder, as it's
-  // scheduled to be removed from the view hierarchy.
-  AppListItemView* released_drag_view =
-      !reparented_into_folder ? drag_view_ : nullptr;
-
   ClearDragState();
   UpdatePaging();
   if (GetWidget()) {
@@ -730,8 +712,8 @@
   StopAutoScroll();
   SetFocusAfterEndDrag();  // Maybe focus the search box.
 
-  AnimateDragIconToTargetPosition(reparented_into_folder, released_drag_view,
-                                  drag_item, folder_item_view);
+  AnimateDragIconToTargetPosition(reparented_into_folder, drag_item,
+                                  folder_item_view);
 }
 
 AppListItemView* AppsGridView::GetItemViewAt(int index) const {
@@ -750,25 +732,11 @@
   // entire apps grid.
   reorder_placeholder_ = view_structure_.GetLastTargetIndex();
 
-  // Create a new AppListItemView to duplicate the original_drag_view in the
-  // folder's grid view.
-  auto view = CreateViewForItem(original_drag_view->item());
   items_need_layer_for_drag_ = true;
-  auto* view_ptr = items_container_->AddChildView(std::move(view));
   for (const auto& entry : view_model_.entries())
     static_cast<AppListItemView*>(entry.view)->EnsureLayer();
-  view_ptr->EnsureLayer();
-  drag_view_ = view_ptr;
 
-  // Dragged view should have focus. This also fixed the issue
-  // https://crbug.com/834682.
-  drag_view_->RequestFocus();
-  drag_view_hider_ = std::make_unique<DragViewHider>(drag_view_);
-
-  // Add drag_view_ to the end of the view_model_.
-  view_model_.Add(drag_view_, view_model_.view_size());
-  view_structure_.Add(drag_view_, view_structure_.GetLastTargetIndex());
-
+  drag_item_ = original_drag_view->item();
   drag_start_grid_view_ = drag_point;
   // Set the flag in root level grid view.
   dragging_for_reparent_item_ = true;
@@ -781,7 +749,7 @@
   // Note that if a cancel ocurrs while reparenting, the |drag_view_| in both
   // root and folder grid views is cleared, so the check in UpdateDragFromItem()
   // for |drag_view_| being nullptr (in the folder grid) is sufficient.
-  DCHECK(drag_view_);
+  DCHECK(drag_item_);
   DCHECK(IsDraggingForReparentInRootLevelGridView());
 
   UpdateDrag(pointer, drag_point);
@@ -792,7 +760,7 @@
 }
 
 bool AppsGridView::IsDraggedView(const AppListItemView* view) const {
-  return drag_view_ == view;
+  return drag_item_ == view->item();
 }
 
 void AppsGridView::ClearDragState() {
@@ -812,15 +780,8 @@
   if (folder_item_reparent_timer_.IsRunning())
     folder_item_reparent_timer_.Stop();
 
-  if (drag_view_) {
-    if (IsDraggingForReparentInRootLevelGridView()) {
-      const int drag_view_index = view_model_.GetIndexOfView(drag_view_);
-      CHECK_EQ(view_model_.view_size() - 1, drag_view_index);
-      DeleteItemViewAtIndex(drag_view_index);
-    }
-  }
   drag_view_ = nullptr;
-
+  drag_item_ = nullptr;
   drag_out_of_folder_container_ = false;
   dragging_for_reparent_item_ = false;
   extra_page_opened_ = false;
@@ -907,9 +868,6 @@
     if (activated_folder_item_view_ == details.child)
       activated_folder_item_view_ = nullptr;
 
-    if (drag_view_ == details.child)
-      EndDrag(true);
-
     if (app_list_features::IsAppGridGhostEnabled()) {
       if (current_ghost_view_ == details.child)
         current_ghost_view_ = nullptr;
@@ -1003,20 +961,18 @@
   }
 }
 
-std::unique_ptr<AppListItemView> AppsGridView::CreateViewForItem(
-    AppListItem* item) {
-  std::unique_ptr<AppListItemView> view =
-      std::make_unique<AppListItemView>(this, item, app_list_view_delegate_);
-  return view;
-}
-
 std::unique_ptr<AppListItemView> AppsGridView::CreateViewForItemAtIndex(
     size_t index) {
   // The |drag_view_| might be pending for deletion, therefore |view_model_|
   // may have one more item than |item_list_|.
   DCHECK_LE(index, item_list_->item_count());
-  auto* item = item_list_->item_at(index);
-  return CreateViewForItem(item);
+  auto view = std::make_unique<AppListItemView>(
+      this, item_list_->item_at(index), app_list_view_delegate_);
+  if (items_need_layer_for_drag_)
+    view->EnsureLayer();
+  if (cardified_state_)
+    view->EnterCardifyState();
+  return view;
 }
 
 void AppsGridView::EnsureViewVisible(const GridIndex& index) {
@@ -1144,9 +1100,6 @@
   CalculateIdealBoundsForFolder();
   for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
-    if (view == drag_view_)
-      continue;
-
     const gfx::Rect& target = view_model_.ideal_bounds(i);
     if (bounds_animator_->GetTargetBounds(view) == target)
       continue;
@@ -1154,7 +1107,8 @@
     const gfx::Rect& current = view->bounds();
     const bool current_visible = visible_bounds.Intersects(current);
     const bool target_visible = visible_bounds.Intersects(target);
-    const bool visible = current_visible || target_visible;
+    const bool visible =
+        !IsViewHiddenForDrag(view) && (current_visible || target_visible);
 
     const int y_diff = target.y() - current.y();
     if (visible && y_diff && y_diff % GetTotalTileSize().height() == 0) {
@@ -1233,7 +1187,7 @@
 }
 
 void AppsGridView::UpdateDropTargetRegion() {
-  DCHECK(drag_view_);
+  DCHECK(drag_item_);
 
   gfx::Point point = last_drag_point_;
   point.set_x(GetMirroredXInView(point.x()));
@@ -1308,7 +1262,6 @@
 
 void AppsGridView::AnimateDragIconToTargetPosition(
     bool dropping_into_folder,
-    AppListItemView* drag_view,
     AppListItem* drag_item,
     AppListItemView* target_folder_view) {
   // If drag icon proxy had not been created, just reshow the drag view.
@@ -1320,19 +1273,24 @@
   // Calculate target item bounds.
   gfx::Rect drag_icon_drop_bounds;
   if (!dropping_into_folder) {
-    DCHECK(drag_view);
+    // Find the view for drag item, and use its ideal bounds to calculate target
+    // drop bounds.
+    for (int i = 0; i < view_model_.view_size(); ++i) {
+      if (view_model_.view_at(i)->item() != drag_item)
+        continue;
 
-    // Get icon bounds in the drag view coordinates.
-    drag_icon_drop_bounds = drag_view->GetIconBounds();
+      auto* drag_view = view_model_.view_at(i);
+      // Get icon bounds in the drag view coordinates.
+      drag_icon_drop_bounds = drag_view->GetIconBounds();
 
-    // Get the expected drag item view location.
-    const int drag_view_model_index = view_model_.GetIndexOfView(drag_view);
-    const gfx::Rect drag_view_ideal_bounds =
-        view_model_.ideal_bounds(drag_view_model_index);
+      // Get the expected drag item view location.
+      const gfx::Rect drag_view_ideal_bounds = view_model_.ideal_bounds(i);
 
-    // Position target icon bounds relative to the ideal drag view bounds.
-    drag_icon_drop_bounds.Offset(drag_view_ideal_bounds.x(),
-                                 drag_view_ideal_bounds.y());
+      // Position target icon bounds relative to the ideal drag view bounds.
+      drag_icon_drop_bounds.Offset(drag_view_ideal_bounds.x(),
+                                   drag_view_ideal_bounds.y());
+      break;
+    }
   } else if (target_folder_view) {
     // Calculate target bounds of dragged item.
     drag_icon_drop_bounds =
@@ -1363,7 +1321,7 @@
 }
 
 bool AppsGridView::DraggedItemCanEnterFolder() {
-  if (!IsFolderItem(drag_view_->item()) && !folder_delegate_)
+  if (!IsFolderItem(drag_item_) && !folder_delegate_)
     return true;
   return false;
 }
@@ -1419,7 +1377,7 @@
 }
 
 bool AppsGridView::DragIsCloseToItem(const gfx::Point& point) {
-  DCHECK(drag_view_);
+  DCHECK(drag_item_);
 
   GridIndex nearest_tile_index = GetNearestTileIndexForPoint(point);
   if (nearest_tile_index == reorder_placeholder_)
@@ -1555,7 +1513,8 @@
   const std::u16string target_view_title = target_view->title()->GetText();
   const bool target_view_is_folder = target_view->is_folder();
 
-  AppListItemView* folder_item = MoveItemToFolder(selected_view_, target_index);
+  AppListItemView* folder_item =
+      MoveItemToFolder(selected_view_->item(), target_index);
   a11y_announcer_->AnnounceKeyboardFoldering(
       moving_view_title, target_view_title, target_view_is_folder);
   DCHECK(folder_item->is_folder());
@@ -1683,7 +1642,7 @@
     bool cancel_drag,
     std::unique_ptr<AppDragIconProxy> drag_icon_proxy) {
   // EndDrag was called before if |drag_view_| is nullptr.
-  if (!drag_view_)
+  if (!drag_item_)
     return;
 
   drag_icon_proxy_ = std::move(drag_icon_proxy);
@@ -1698,12 +1657,13 @@
   // This is the folder view to drop an item into. Cache the |drag_view_|'s item
   // and its bounds for later use in folder dropping animation.
   AppListItemView* folder_item_view = nullptr;
+  AppListItem* drag_item = drag_item_;
 
   if (!events_forwarded_to_drag_drop_host && !cancel_reparent) {
     UpdateDropTargetRegion();
     if (drop_target_region_ == ON_ITEM && DropTargetIsValidFolder() &&
         DraggedItemCanEnterFolder()) {
-      cancel_reparent = !ReparentItemToAnotherFolder(drag_view_, drop_target_);
+      cancel_reparent = !ReparentItemToAnotherFolder(drag_item, drop_target_);
       // Announce folder dropping event before end of drag of reparented item.
       MaybeCreateFolderDroppingAccessibilityEvent();
       if (!cancel_reparent) {
@@ -1714,7 +1674,7 @@
       }
     } else if (drop_target_region_ != NO_TARGET &&
                IsValidReorderTargetIndex(drop_target_)) {
-      ReparentItemForReorder(drag_view_, drop_target_);
+      ReparentItemForReorder(drag_item, drop_target_);
       RecordAppMovingTypeMetrics(kMoveByDragOutOfFolder);
       // Announce accessibility event before the end of drag for reparented
       // item.
@@ -1726,25 +1686,12 @@
 
   SetAsFolderDroppingTarget(drop_target_, false);
 
-  // ClearDragState will delete the drag view if reparent is canceled - cache
-  // the target app list item, as it is needed to calculate target icon drop
-  // bounds.
-  AppListItem* drag_item = drag_view_->item();
-
   // Hide the drag item icon from the target folder icon.
   if (folder_item_view) {
     folder_icon_item_hider_ = std::make_unique<FolderIconItemHider>(
         static_cast<AppListFolderItem*>(folder_item_view->item()), drag_item);
   }
 
-  AppListItemView* released_drag_view = nullptr;
-  if (!cancel_reparent) {
-    // By setting |drag_view_| to nullptr here, we prevent ClearDragState() from
-    // cleaning up the newly created AppListItemView, effectively claiming
-    // ownership of the newly created drag view.
-    released_drag_view = drag_view_;
-    drag_view_ = nullptr;
-  }
   UpdatePaging();
   ClearDragState();
 
@@ -1753,7 +1700,8 @@
   else
     AnimateToIdealBounds();
 
-  view_structure_.SaveToMetadata();
+  if (!cancel_reparent)
+    view_structure_.SaveToMetadata();
 
   // Hide the |current_ghost_view_| after completed drag from within
   // folder to |apps_grid_view_|.
@@ -1763,8 +1711,8 @@
   SetFocusAfterEndDrag();  // Maybe focus the search box.
 
   AnimateDragIconToTargetPosition(
-      /*dropping_into_folder=*/cancel_reparent || folder_item_view,
-      released_drag_view, drag_item, folder_item_view);
+      /*dropping_into_folder=*/cancel_reparent || folder_item_view, drag_item,
+      folder_item_view);
 }
 
 void AppsGridView::EndDragForReparentInHiddenFolderGridView() {
@@ -1793,19 +1741,13 @@
   DCHECK(!folder_delegate_);
   DCHECK(activated_folder_item_view_);
 
-  auto* reparented_view_in_root_grid = items_container_->AddChildView(
-      CreateViewForItem(reparented_view->item()));
-  view_model_.Add(reparented_view_in_root_grid, view_model_.view_size());
-  view_structure_.Add(reparented_view_in_root_grid,
-                      view_structure_.GetLastTargetIndex());
-
   // Set |activated_folder_item_view_| selected so |target_index| will be
   // computed relative to the open folder.
   SetSelectedView(activated_folder_item_view_);
   const GridIndex target_index =
       GetTargetGridIndexForKeyboardReparent(key_code);
   AnnounceReorder(target_index);
-  ReparentItemForReorder(reparented_view_in_root_grid, target_index);
+  ReparentItemForReorder(reparented_view->item(), target_index);
 
   GetViewAtIndex(target_index)->RequestFocus();
   Layout();
@@ -1964,44 +1906,43 @@
   }
 }
 
-void AppsGridView::MoveItemInModel(AppListItemView* item_view,
-                                   const GridIndex& target) {
-  int current_model_index = view_model_.GetIndexOfView(item_view);
-  CHECK_GE(current_model_index, 0);
+void AppsGridView::MoveItemInModel(AppListItem* item, const GridIndex& target) {
+  const std::string item_id = item->id();
+
   size_t current_item_list_index = 0;
-  bool found = item_list_->FindItemIndex(item_view->item()->id(),
-                                         &current_item_list_index);
+  bool found = item_list_->FindItemIndex(item_id, &current_item_list_index);
   CHECK(found);
 
-  int target_model_index =
-      view_structure_.GetTargetModelIndexForMove(item_view, target);
   size_t target_item_list_index =
-      view_structure_.GetTargetItemListIndexForMove(item_view, target);
-  // The same item index does not guarantee the same visual index, so move the
-  // item visual index here.
-  view_structure_.Move(item_view, target);
+      view_structure_.GetTargetItemListIndexForMove(item, target);
 
-  DVLOG(1) << "MoveItemInModel: view model: " << current_model_index << " -> "
-           << target_model_index << ", item list: " << current_item_list_index
-           << " -> " << target_item_list_index;
+  const bool moving_to_new_page =
+      !item->IsInFolder() &&
+      (target.page == pagination_model_.total_pages() ||
+       (target.page == pagination_model_.total_pages() - 1 &&
+        view_structure_.GetLastTargetIndexOfPage(target.page).slot == 0));
 
-  // Reorder the app list item views in accordance with |view_model_|.
-  items_container_->ReorderChildView(item_view, target_model_index);
-  items_container_->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
-                                             true /* send_native_event */);
+  {
+    ScopedModelUpdate update(this);
+    item_list_->MoveItem(current_item_list_index, target_item_list_index);
 
-  if (target_item_list_index == current_item_list_index)
-    return;
-
-  item_list_->RemoveObserver(this);
-  item_list_->MoveItem(current_item_list_index, target_item_list_index);
-  view_model_.Move(current_model_index, target_model_index);
-  item_list_->AddObserver(this);
+    // If the item is being moved to a new page, ensure that it's preceded by a
+    // page break.
+    if (moving_to_new_page) {
+      size_t final_item_list_index = 0;
+      if (item_list_->FindItemIndex(item_id, &final_item_list_index) &&
+          final_item_list_index > 0 &&
+          !item_list_->item_at(final_item_list_index - 1)->is_page_break()) {
+        item_list_->AddPageBreakItemAfter(
+            item_list_->item_at(final_item_list_index - 1));
+      }
+    }
+  }
 }
 
-AppListItemView* AppsGridView::MoveItemToFolder(AppListItemView* item_view,
+AppListItemView* AppsGridView::MoveItemToFolder(AppListItem* item,
                                                 const GridIndex& target) {
-  const std::string& source_item_id = item_view->item()->id();
+  const std::string& source_item_id = item->id();
   AppListItemView* target_view =
       GetViewDisplayedAtSlotOnCurrentPage(target.slot);
   DCHECK(target_view);
@@ -2012,139 +1953,54 @@
   // item (e.g., if a folder drop does not clean up properly).
   DCHECK_NE(source_item_id, target_view_item_id);
 
+  const bool is_dragged_view = drag_item_ == item;
   // Make change to data model.
-  item_list_->RemoveObserver(this);
-  std::string folder_item_id =
-      model_->MergeItems(target_view_item_id, source_item_id);
-  item_list_->AddObserver(this);
+  std::string folder_item_id;
+
+  {
+    ScopedModelUpdate update(this);
+    folder_item_id = model_->MergeItems(target_view_item_id, source_item_id);
+  }
+
   if (folder_item_id.empty()) {
     LOG(ERROR) << "Unable to merge into item id: " << target_view_item_id;
     return nullptr;
   }
-  if (folder_item_id != target_view_item_id) {
-    // New folder was created, change the view model to replace the old target
-    // view with the new folder item view.
-    size_t folder_item_index;
-    if (item_list_->FindItemIndex(folder_item_id, &folder_item_index)) {
-      // Paged view structure may get updated in two steps:
-      // 1. Item view deletion.
-      // 2. Addition of the new target view.
-      // Make sure the view structure is not sanitized between those two steps.
-      std::unique_ptr<PagedViewStructure::ScopedSanitizeLock> sanitize_lock =
-          view_structure_.GetSanitizeLock();
 
-      int target_model_index = view_model_.GetIndexOfView(target_view);
-      GridIndex target_index = GetIndexOfView(target_view);
-      gfx::Rect target_view_bounds = target_view->bounds();
-      DeleteItemViewAtIndex(target_model_index);
-      std::unique_ptr<AppListItemView> new_target_view =
-          CreateViewForItemAtIndex(folder_item_index);
-      new_target_view->SetBoundsRect(target_view_bounds);
-      view_model_.Add(new_target_view.get(), target_model_index);
-      view_structure_.Add(new_target_view.get(), target_index);
-
-      // If drag view is in front of the position where it will be moved to, we
-      // should skip it.
-      const int offset = (drag_view_ && view_model_.GetIndexOfView(drag_view_) <
-                                            target_model_index)
-                             ? 1
-                             : 0;
-      target_view = items_container_->AddChildViewAt(
-          std::move(new_target_view), target_model_index - offset);
-    } else {
-      LOG(ERROR) << "Folder no longer in item_list: " << folder_item_id;
-    }
+  const AppListItem* const folder_item = item_list_->FindItem(folder_item_id);
+  if (!folder_item) {
+    LOG(ERROR) << "Unable to find target folder item " << folder_item_id;
+    return nullptr;
   }
 
-  FadeOutItemViewAndDelete(item_view);
-  if (drag_view_ == item_view)
+  if (is_dragged_view)
     RecordAppMovingTypeMetrics(kMoveByDragIntoFolder);
 
-  return target_view;
+  return GetItemViewAt(GetModelIndexOfItem(folder_item));
 }
 
-void AppsGridView::FadeOutItemViewAndDelete(AppListItemView* item_view) {
-  const int model_index = view_model_.GetIndexOfView(item_view);
-
-  view_model_.Remove(model_index);
-  view_structure_.Remove(item_view);
-  item_view->title()->SetVisible(false);
-  bounds_animator_->AnimateViewTo(item_view, item_view->bounds());
-  bounds_animator_->SetAnimationDelegate(
-      item_view, std::unique_ptr<gfx::AnimationDelegate>(
-                     new ItemRemoveAnimationDelegate(item_view)));
-}
-
-void AppsGridView::ReparentItemForReorder(AppListItemView* item_view,
+void AppsGridView::ReparentItemForReorder(AppListItem* item,
                                           const GridIndex& target) {
-  item_list_->RemoveObserver(this);
-  model_->RemoveObserver(this);
+  DCHECK(item->IsInFolder());
 
-  AppListItem* reparent_item = item_view->item();
-  DCHECK(reparent_item->IsInFolder());
-  const std::string source_folder_id = reparent_item->folder_id();
-  AppListFolderItem* source_folder =
-      static_cast<AppListFolderItem*>(item_list_->FindItem(source_folder_id));
-
-  int target_model_index =
-      view_structure_.GetTargetModelIndexForMove(item_view, target);
+  const std::string source_folder_id = item->folder_id();
   int target_item_index =
-      view_structure_.GetTargetItemListIndexForMove(item_view, target);
-
-  // Paged view structure may get updated in two steps:
-  // 1. (Folder) item view deletion.
-  // 2. Item move to a new position.
-  // Make sure the view structure is not sanitized between those two steps.
-  std::unique_ptr<PagedViewStructure::ScopedSanitizeLock> sanitize_lock =
-      view_structure_.GetSanitizeLock();
-
-  // Remove the source folder view if there is only 1 item in it, since the
-  // source folder will be deleted after its only child item removed from it.
-  GridIndex target_override = target;
-  if (source_folder->ChildItemCount() == 1u) {
-    const int deleted_folder_index =
-        view_model_.GetIndexOfView(activated_folder_item_view_);
-    const GridIndex deleted_folder_grid_index =
-        GetIndexOfView(activated_folder_item_view_);
-    DeleteItemViewAtIndex(deleted_folder_index);
-
-    // Adjust |target_model_index| if it is beyond the deleted folder index.
-    if (target_model_index > deleted_folder_index) {
-      --target_model_index;
-
-      // Do not decrement |target_item_index| since the folder item has not been
-      // removed from the item list yet.
-    }
-
-    // Adjust |target_override| if it is beyond the deleted folder grid index in
-    // the same page.
-    if (!folder_delegate_ && target.page == deleted_folder_grid_index.page &&
-        target.slot > deleted_folder_grid_index.slot) {
-      --target_override.slot;
-    }
-  }
+      view_structure_.GetTargetItemListIndexForMove(item, target);
 
   // Move the item from its parent folder to top level item list.
-  // Must move to target_model_index, the location we expect the target item
-  // to be, not the item location we want to insert before.
-  int current_model_index = view_model_.GetIndexOfView(item_view);
   syncer::StringOrdinal target_position;
   if (target_item_index < static_cast<int>(item_list_->item_count()))
     target_position = item_list_->item_at(target_item_index)->position();
-  model_->MoveItemToFolderAt(reparent_item, "", target_position);
-  view_model_.Move(current_model_index, target_model_index);
-  view_structure_.Move(item_view, target_override);
-  items_container_->ReorderChildView(item_view, target_model_index);
-  items_container_->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
-                                             true /* send_native_event */);
+
+  {
+    ScopedModelUpdate update(this);
+    model_->MoveItemToFolderAt(item, "", target_position);
+  }
 
   RemoveLastItemFromReparentItemFolderIfNecessary(source_folder_id);
-
-  item_list_->AddObserver(this);
-  model_->AddObserver(this);
 }
 
-bool AppsGridView::ReparentItemToAnotherFolder(AppListItemView* item_view,
+bool AppsGridView::ReparentItemToAnotherFolder(AppListItem* item,
                                                const GridIndex& target) {
   DCHECK(IsDraggingForReparentInRootLevelGridView());
 
@@ -2153,88 +2009,29 @@
   if (!target_view)
     return false;
 
-  AppListItem* reparent_item = item_view->item();
-  DCHECK(reparent_item->IsInFolder());
-  const std::string source_folder_id = reparent_item->folder_id();
-  AppListFolderItem* source_folder =
-      static_cast<AppListFolderItem*>(item_list_->FindItem(source_folder_id));
+  CHECK(item->IsInFolder());
+  const std::string source_folder_id = item->folder_id();
 
   AppListItem* target_item = target_view->item();
 
   // An app is being reparented to its original folder. Just cancel the
   // reparent.
-  if (target_item->id() == reparent_item->folder_id())
+  if (target_item->id() == item->folder_id())
     return false;
 
-  // Make change to data model.
-  item_list_->RemoveObserver(this);
-
-  // Paged view structure may get updated in several steps:
-  // 1.  (Folder) item view deletion (if the folder has no items left).
-  // 2.  Deletion of item view on which the drag view was dropped (if a new
-  //     folder was created).
-  // 3.  Addition of a new folder view (if a new folder was created).
-  // 4.  Removal of the last item from the source folder (if only one item was
-  //     left).
-  // 5.  Removal of the drag view.
-  // Make sure the view structure is not sanitized between those steps.
-  std::unique_ptr<PagedViewStructure::ScopedSanitizeLock> sanitize_lock =
-      view_structure_.GetSanitizeLock();
-
-  // Remove the source folder view if there is only 1 item in it, since the
-  // source folder will be deleted after its only child item merged into the
-  // target item.
-  if (source_folder->ChildItemCount() == 1u) {
-    DeleteItemViewAtIndex(
-        view_model_.GetIndexOfView(activated_folder_item_view()));
+  std::string target_id_after_merge;
+  {
+    ScopedModelUpdate update(this);
+    target_id_after_merge = model_->MergeItems(target_item->id(), item->id());
   }
 
-  // Move item to the target folder.
-  std::string target_id_after_merge =
-      model_->MergeItems(target_item->id(), reparent_item->id());
   if (target_id_after_merge.empty()) {
     LOG(ERROR) << "Unable to reparent to item id: " << target_item->id();
-    item_list_->AddObserver(this);
     return false;
   }
 
-  if (target_id_after_merge != target_item->id()) {
-    // New folder was created, change the view model to replace the old target
-    // view with the new folder item view.
-    const std::string& new_folder_id = reparent_item->folder_id();
-    size_t new_folder_index;
-    if (item_list_->FindItemIndex(new_folder_id, &new_folder_index)) {
-      // Save the target view's bounds before deletion, which will be used as
-      // new folder view's bounds.
-      gfx::Rect target_rect = target_view->bounds();
-      int target_model_index = view_model_.GetIndexOfView(target_view);
-      GridIndex target_index = GetIndexOfView(target_view);
-      DeleteItemViewAtIndex(target_model_index);
-      std::unique_ptr<AppListItemView> new_folder_view =
-          CreateViewForItemAtIndex(new_folder_index);
-      new_folder_view->SetBoundsRect(target_rect);
-      view_model_.Add(new_folder_view.get(), target_model_index);
-      view_structure_.Add(new_folder_view.get(), target_index);
-      items_container_->AddChildViewAt(std::move(new_folder_view),
-                                       target_model_index);
-    } else {
-      LOG(ERROR) << "Folder no longer in item_list: " << new_folder_id;
-    }
-  }
-
   RemoveLastItemFromReparentItemFolderIfNecessary(source_folder_id);
 
-  item_list_->AddObserver(this);
-
-  // Fade out the drag_view_ and delete it when animation ends.
-  int drag_model_index = view_model_.GetIndexOfView(drag_view_);
-  view_model_.Remove(drag_model_index);
-  view_structure_.Remove(drag_view_);
-  bounds_animator_->AnimateViewTo(drag_view_, drag_view_->bounds());
-  bounds_animator_->SetAnimationDelegate(
-      drag_view_, std::unique_ptr<gfx::AnimationDelegate>(
-                      new ItemRemoveAnimationDelegate(drag_view_)));
-
   RecordAppMovingTypeMetrics(kMoveIntoAnotherFolder);
   return true;
 }
@@ -2250,25 +2047,6 @@
   if (!source_folder || (source_folder && !source_folder->ShouldAutoRemove()))
     return;
 
-  // Save the folder item view's bounds before deletion, which will be used as
-  // last item view's bounds.
-  gfx::Rect folder_rect = activated_folder_item_view()->bounds();
-  const GridIndex target_index = GetIndexOfView(activated_folder_item_view());
-  const int target_model_index =
-      view_model_.GetIndexOfView(activated_folder_item_view());
-
-  // Removing last item from the folder may require several paged view
-  // structure updates:
-  // 1.  Delete the folder view.
-  // 2.  Add an item view for the last folder item to the root level grid.
-  // Ensure the view structure is not sanitized between those steps.
-  std::unique_ptr<PagedViewStructure::ScopedSanitizeLock> sanitize_lock =
-      view_structure_.GetSanitizeLock();
-
-  // Delete view associated with the folder item to be removed.
-  DeleteItemViewAtIndex(
-      view_model_.GetIndexOfView(activated_folder_item_view()));
-
   // For single-app folders (which can exist for system-managed folders, see
   // crbug.com/925052) there will not be a "last item" so we can ignore the
   // rest.
@@ -2277,22 +2055,8 @@
 
   // Now make the data change to remove the folder item in model.
   AppListItem* last_item = source_folder->item_list()->item_at(0);
+  ScopedModelUpdate update(this);
   model_->MoveItemToFolderAt(last_item, "", source_folder->position());
-
-  // Create a new item view for the last item in folder.
-  size_t last_item_index;
-  if (!item_list_->FindItemIndex(last_item->id(), &last_item_index) ||
-      last_item_index > item_list_->item_count()) {
-    NOTREACHED();
-    return;
-  }
-  std::unique_ptr<AppListItemView> last_item_view =
-      CreateViewForItemAtIndex(last_item_index);
-  last_item_view->SetBoundsRect(folder_rect);
-  view_model_.Add(last_item_view.get(), target_model_index);
-  view_structure_.Add(last_item_view.get(), target_index);
-  items_container_->AddChildViewAt(std::move(last_item_view),
-                                   target_model_index);
 }
 
 void AppsGridView::CancelContextMenusOnCurrentPage() {
@@ -2321,41 +2085,59 @@
 }
 
 void AppsGridView::OnListItemAdded(size_t index, AppListItem* item) {
-  EndDrag(true);
+  if (!updating_model_)
+    EndDrag(true);
 
   if (!item->is_page_break()) {
     std::unique_ptr<AppListItemView> view = CreateViewForItemAtIndex(index);
+    if (view->item() == drag_item_) {
+      drag_view_ = view.get();
+      drag_view_->RequestFocus();
+      drag_view_hider_ = std::make_unique<DragViewHider>(drag_view_);
+    }
     int model_index = GetTargetModelIndexFromItemIndex(index);
     view_model_.Add(view.get(), model_index);
     items_container_->AddChildViewAt(std::move(view), model_index);
   }
 
   view_structure_.LoadFromMetadata();
-  UpdateColsAndRowsForFolder();
-  UpdatePaging();
-  UpdatePulsingBlockViews();
+
+  // If model update is in progress, paging should be updated when the operation
+  // that caused the model update completes.
+  if (!updating_model_) {
+    UpdatePaging();
+    UpdateColsAndRowsForFolder();
+    UpdatePulsingBlockViews();
+  }
+
   Layout();
-  SchedulePaint();
 }
 
 void AppsGridView::OnListItemRemoved(size_t index, AppListItem* item) {
-  EndDrag(true);
+  if (!updating_model_)
+    EndDrag(true);
 
   if (!item->is_page_break())
     DeleteItemViewAtIndex(GetModelIndexOfItem(item));
 
   view_structure_.LoadFromMetadata();
-  UpdateColsAndRowsForFolder();
-  UpdatePaging();
-  UpdatePulsingBlockViews();
+
+  // If model update is in progress, paging should be updated when the operation
+  // that caused the model update completes.
+  if (!updating_model_) {
+    UpdatePaging();
+    UpdateColsAndRowsForFolder();
+    UpdatePulsingBlockViews();
+  }
+
   Layout();
-  SchedulePaint();
 }
 
 void AppsGridView::OnListItemMoved(size_t from_index,
                                    size_t to_index,
                                    AppListItem* item) {
-  EndDrag(true);
+  if (!updating_model_)
+    EndDrag(true);
 
   if (item->is_page_break()) {
     LOG(ERROR) << "Page break item is moved: " << item->id();
@@ -2373,9 +2155,16 @@
   }
 
   view_structure_.LoadFromMetadata();
-  UpdateColsAndRowsForFolder();
-  UpdatePaging();
-  if (GetWidget() && GetWidget()->IsVisible())
+
+  // If model update is in progress, paging should be updated when the operation
+  // that caused the model update completes.
+  if (!updating_model_) {
+    UpdatePaging();
+    UpdateColsAndRowsForFolder();
+    UpdatePulsingBlockViews();
+  }
+
+  if (!updating_model_ && GetWidget() && GetWidget()->IsVisible())
     AnimateToIdealBounds();
   else
     Layout();
@@ -2391,7 +2180,7 @@
 }
 
 void AppsGridView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
-  if (drag_view_ || drag_icon_proxy_)
+  if (drag_item_ || drag_icon_proxy_)
     return;
 
   if (bounds_animation_for_cardified_state_in_progress_ ||
@@ -2618,9 +2407,6 @@
 
   handling_keyboard_move_ = true;
 
-  if (target_index.page == pagination_model_.total_pages())
-    view_structure_.AppendPage();
-
   AppListItemView* original_selected_view = selected_view_;
   const GridIndex original_selected_view_index =
       GetIndexOfView(original_selected_view);
@@ -2638,11 +2424,10 @@
     // a full page results in the last item being pushed to the next page.
     std::unique_ptr<PagedViewStructure::ScopedSanitizeLock> sanitize_lock =
         view_structure_.GetSanitizeLock();
-    MoveItemInModel(selected_view_, target_index);
-    view_structure_.SaveToMetadata();
+    MoveItemInModel(selected_view_->item(), target_index);
     if (swap_items) {
       DCHECK(target_view);
-      MoveItemInModel(target_view, original_selected_view_index);
+      MoveItemInModel(target_view->item(), original_selected_view_index);
     }
   }
 
@@ -2803,8 +2588,10 @@
 }
 
 void AppsGridView::MaybeCreateFolderDroppingAccessibilityEvent() {
+  if (!drag_item_ || !drag_view_)
+    return;
   if (drop_target_region_ != ON_ITEM || !DropTargetIsValidFolder() ||
-      IsFolderItem(drag_view_->item()) || folder_delegate_ ||
+      IsFolderItem(drag_item_) || folder_delegate_ ||
       drop_target_ == last_folder_dropping_a11y_event_location_) {
     return;
   }
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index ad705301..b999f39 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -170,9 +170,11 @@
   bool has_selected_view() const { return selected_view_ != nullptr; }
   AppListItemView* selected_view() const { return selected_view_; }
 
-  bool has_dragged_view() const { return drag_view_ != nullptr; }
   const AppListItemView* drag_view() const { return drag_view_; }
 
+  bool has_dragged_item() const { return drag_item_ != nullptr; }
+  const AppListItem* drag_item() const { return drag_item_; }
+
   // Gets the PaginationModel used for the grid view.
   PaginationModel* pagination_model() { return &pagination_model_; }
 
@@ -439,6 +441,11 @@
   // drag state is cleared complete.
   bool items_need_layer_for_drag_ = false;
 
+  // The `AppListItemView` that is being dragged within the apps grid (i.e. the
+  // AppListItemView for `drag_item_`) if the drag item is currently part of the
+  // item list shown in the apps grid. `drag_view_` may be nullptr during item
+  // reparent drag while being handled in the root app list grid (the drag item
+  // will be added to target item list only when the drag ends).
   // Subclasses need non-const access.
   AppListItemView* drag_view_ = nullptr;
 
@@ -502,8 +509,6 @@
   // number of apps.
   void UpdatePulsingBlockViews();
 
-  std::unique_ptr<AppListItemView> CreateViewForItem(AppListItem* item);
-
   std::unique_ptr<AppListItemView> CreateViewForItemAtIndex(size_t index);
 
   // Ensures the view is visible. Note that if there is a running page
@@ -568,34 +573,29 @@
   void DispatchDragEventToDragAndDropHost(
       const gfx::Point& location_in_screen_coordinates);
 
-  // Updates |model_| to move item represented by |item_view| to |target| slot.
-  // Pushes all items from |item_view|'s GridIndex + 1 to |target| back by 1
-  // GridIndex slot.
-  void MoveItemInModel(AppListItemView* item_view, const GridIndex& target);
+  // Updates `model_` to move `item` to `target` slot.
+  void MoveItemInModel(AppListItem* item, const GridIndex& target);
 
-  // Updates |model_| to move item represented by |item_view| into a folder
-  // containing item located at |target| slot, also update |view_model_| for
-  // the related view changes. Returns the preexisting or created folder as a
-  // result of the move, or nullptr if the move fails.
-  AppListItemView* MoveItemToFolder(AppListItemView* item_view,
-                                    const GridIndex& target);
+  // Updates `model_` to move `item` into a folder containing item located at
+  // `target` slot. Returns the preexisting or created folder view as a result
+  // of the move, or nullptr if the move fails.
+  AppListItemView* MoveItemToFolder(AppListItem* item, const GridIndex& target);
 
-  // Sets up |item_view| to fade out and delete on animation end.
-  void FadeOutItemViewAndDelete(AppListItemView* item_view);
+  // Updates data model for re-parenting a folder item to a new position in top
+  // level item list. The view model is will get updated in response to the data
+  // model changes.
+  void ReparentItemForReorder(AppListItem* item, const GridIndex& target);
 
-  // Updates both data model and view_model_ for re-parenting a folder item to a
-  // new position in top level item list.
-  void ReparentItemForReorder(AppListItemView* item_view,
-                              const GridIndex& target);
-
-  // Updates both data model and view_model_ for re-parenting a folder item
-  // to anther folder target. Returns whether the reparent succeeded.
-  bool ReparentItemToAnotherFolder(AppListItemView* item_view,
+  // Updates both data model for re-parenting a folder item
+  // to anther folder target. The view model will get updated in response to the
+  // data model changes.
+  // Returns whether the reparent succeeded.
+  bool ReparentItemToAnotherFolder(AppListItem* item_view,
                                    const GridIndex& target);
 
   // If there is only 1 item left in the source folder after reparenting an item
-  // from it, updates both data model and view_model_ for removing last item
-  // from the source folder and removes the source folder.
+  // from it, updates data model to remove last item from the source folder and
+  // remove the source folder.
   void RemoveLastItemFromReparentItemFolderIfNecessary(
       const std::string& source_folder_id);
 
@@ -622,14 +622,10 @@
   // set.
   // `dropping_into_folder` - Whether the drag item icon should be dropped
   // into a folder view.
-  // `drag_view` - The view showing the drag item. Used to calculate target
-  // bounds when the item is dropped into the root apps grid. Can be nullptr
-  // otherwise.
   // `drag_item` - The dragged item.
   // `target_folder_view` - If the item needs to be dropped into a folder, the
   // target folder view.
   void AnimateDragIconToTargetPosition(bool dropping_into_folder,
-                                       AppListItemView* drag_view,
                                        AppListItem* drag_item,
                                        AppListItemView* target_folder_view);
 
@@ -755,6 +751,8 @@
   // Invoked when |host_drag_start_timer_| fires.
   void OnHostDragStartTimerFired();
 
+  class ScopedModelUpdate;
+
   AppListModel* model_ = nullptr;         // Owned by AppListView.
   AppListItemList* item_list_ = nullptr;  // Not owned.
 
@@ -782,6 +780,11 @@
 
   AppListItemView* selected_view_ = nullptr;
 
+  // Set while the AppsGridView is handling drag operation for an app list item.
+  // It's set to the drag item that is being dragged in the UI. If `drag_view_`
+  // is set, it should have the same value as `drag_view_->item()`.
+  AppListItem* drag_item_ = nullptr;
+
   // The index of the drag_view_ when the drag starts.
   GridIndex drag_view_init_index_;
 
@@ -790,6 +793,9 @@
 
   Pointer drag_pointer_ = NONE;
 
+  // Whether the apps grid is currently updating the app list model.
+  bool updating_model_ = false;
+
   // Object that while in scope hides the drag view from the UI during the drag
   // operation. Note that this may remain set even after ClearDragState(), while
   // the drag icon proxy animation is in progress.
@@ -849,9 +855,6 @@
   // True if the drag_view_ item is a folder item being dragged for reparenting.
   bool dragging_for_reparent_item_ = false;
 
-  // True if it is the end gesture from shelf dragging.
-  bool is_end_gesture_ = false;
-
   // The drop location of the most recent reorder related accessibility event.
   GridIndex last_reorder_a11y_event_location_;
 
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 917a59d9..8eedd9c 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -408,7 +408,7 @@
 
   // Calls the private method.
   void MoveItemInModel(AppListItemView* item_view, const GridIndex& target) {
-    apps_grid_view_->MoveItemInModel(item_view, target);
+    apps_grid_view_->MoveItemInModel(item_view->item(), target);
   }
 
   TestAppListColorProvider color_provider_;      // Needed by AppListView.
@@ -2017,6 +2017,8 @@
 TEST_F(AppsGridViewTest, ControlArrowSwapsBetweenFullPages) {
   const int kPages = 3;
   model_->PopulateApps(kPages * GetTilesPerPage());
+  apps_grid_view_->UpdatePagedViewStructure();
+
   // For every item in the first row, ensure an upward move results in the item
   // swapping places with the item directly above it.
   for (int i = 0; i < apps_grid_view_->cols(); ++i) {
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 109b586..86dd864 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -176,11 +176,11 @@
 }
 
 void ContentsView::CancelDrag() {
-  if (apps_container_view_->apps_grid_view()->has_dragged_view())
+  if (apps_container_view_->apps_grid_view()->has_dragged_item())
     apps_container_view_->apps_grid_view()->EndDrag(true);
   if (apps_container_view_->app_list_folder_view()
           ->items_grid_view()
-          ->has_dragged_view()) {
+          ->has_dragged_item()) {
     apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
         true);
   }
diff --git a/ash/app_list/views/paged_apps_grid_view.cc b/ash/app_list/views/paged_apps_grid_view.cc
index 6c665ff..9308937 100644
--- a/ash/app_list/views/paged_apps_grid_view.cc
+++ b/ash/app_list/views/paged_apps_grid_view.cc
@@ -629,8 +629,6 @@
                                             int new_selected) {
   items_container()->layer()->SetTransform(gfx::Transform());
   if (IsDragging()) {
-    drag_view_->layer()->SetTransform(gfx::Transform());
-
     // Sets the transform to locate the scrolled content.
     gfx::Size grid_size = GetTileGridSize();
     gfx::Vector2d update;
@@ -971,10 +969,7 @@
   RecenterItemsContainer();
 
   // Drag view can be nullptr or moved from the model by EndDrag.
-  const bool model_contains_drag_view =
-      drag_view_ && (view_model()->GetIndexOfView(drag_view_) != -1);
-  const int number_of_views_to_animate =
-      view_model()->view_size() - (model_contains_drag_view ? 1 : 0);
+  const int number_of_views_to_animate = view_model()->view_size();
 
   base::RepeatingClosure on_bounds_animator_callback;
   if (number_of_views_to_animate > 0) {
@@ -989,9 +984,6 @@
       0, start_position.y() - items_container()->origin().y());
   for (int i = 0; i < view_model()->view_size(); ++i) {
     AppListItemView* entry_view = view_model()->view_at(i);
-    // We don't animate bounds for the dragged view.
-    if (entry_view == drag_view_)
-      continue;
     // Reposition view bounds to compensate for the translation offset.
     gfx::Rect current_bounds = entry_view->bounds();
     current_bounds.Offset(translate_offset);
@@ -1006,6 +998,11 @@
     gfx::Rect target_bounds(view_model()->ideal_bounds(i));
     entry_view->SetBoundsRect(target_bounds);
 
+    if (IsViewHiddenForDrag(entry_view)) {
+      on_bounds_animator_callback.Run();
+      continue;
+    }
+
     // View bounds are currently |target_bounds|. Transform the view so it
     // appears in |current_bounds|. Note that bounds are flipped by views in
     // RTL UI direction, which is not taken into account by
diff --git a/ash/app_list/views/scrollable_apps_grid_view.cc b/ash/app_list/views/scrollable_apps_grid_view.cc
index ac1559c..83b0ba3 100644
--- a/ash/app_list/views/scrollable_apps_grid_view.cc
+++ b/ash/app_list/views/scrollable_apps_grid_view.cc
@@ -81,14 +81,7 @@
   CalculateIdealBoundsForFolder();
   for (int i = 0; i < view_model()->view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
-    if (view != drag_view()) {
-      view->SetBoundsRect(view_model()->ideal_bounds(i));
-    } else {
-      // If the drag view size changes, make sure it has the same center.
-      gfx::Rect bounds = view->bounds();
-      bounds.ClampToCenteredSize(GetTileViewSize());
-      view->SetBoundsRect(bounds);
-    }
+    view->SetBoundsRect(view_model()->ideal_bounds(i));
   }
   views::ViewModelUtils::SetViewBoundsToIdealBounds(pulsing_blocks_model());
 }
diff --git a/ash/components/power/dark_resume_controller.cc b/ash/components/power/dark_resume_controller.cc
index 3c2d6fc..247a0c5 100644
--- a/ash/components/power/dark_resume_controller.cc
+++ b/ash/components/power/dark_resume_controller.cc
@@ -9,7 +9,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 
-namespace chromeos {
+namespace ash {
 namespace system {
 
 namespace {
@@ -149,4 +149,4 @@
 }
 
 }  // namespace system
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/components/power/dark_resume_controller.h b/ash/components/power/dark_resume_controller.h
index 2272afd..468fcfc 100644
--- a/ash/components/power/dark_resume_controller.h
+++ b/ash/components/power/dark_resume_controller.h
@@ -17,7 +17,7 @@
 #include "services/device/public/mojom/wake_lock.mojom.h"
 #include "services/device/public/mojom/wake_lock_provider.mojom.h"
 
-namespace chromeos {
+namespace ash {
 namespace system {
 
 // This class listens to dark resume events from the power manager and makes
@@ -132,6 +132,13 @@
 };
 
 }  // namespace system
+}  // namespace ash
+
+// TODO(https://crbug.com/1164001): remove when ChromeOS code migration is done.
+namespace chromeos {
+namespace system {
+using ::ash::system::DarkResumeController;
+}  // namespace system
 }  // namespace chromeos
 
 #endif  // ASH_COMPONENTS_POWER_DARK_RESUME_CONTROLLER_H_
diff --git a/ash/components/power/dark_resume_controller_unittest.cc b/ash/components/power/dark_resume_controller_unittest.cc
index 9409705..17050c2 100644
--- a/ash/components/power/dark_resume_controller_unittest.cc
+++ b/ash/components/power/dark_resume_controller_unittest.cc
@@ -15,16 +15,16 @@
 #include "services/device/public/cpp/test/test_wake_lock_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 namespace system {
 
 namespace {
 
+using device::mojom::WakeLockType;
+
 constexpr char kWakeLockDescription[] = "DarkResumeTest";
 
-}
-
-using device::mojom::WakeLockType;
+}  // namespace
 
 class DarkResumeControllerTest : public testing::Test {
  public:
@@ -188,4 +188,4 @@
 }
 
 }  // namespace system
-}  // namespace chromeos
+}  // namespace ash
diff --git a/ash/display/cursor_window_controller.cc b/ash/display/cursor_window_controller.cc
index dd3d3dc..b79ee77f 100644
--- a/ash/display/cursor_window_controller.cc
+++ b/ash/display/cursor_window_controller.cc
@@ -4,8 +4,6 @@
 
 #include "ash/display/cursor_window_controller.h"
 
-#include <utility>
-
 #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/capture_mode/capture_mode_session.h"
@@ -323,16 +321,23 @@
   if (!is_cursor_compositing_enabled_)
     return;
 
-  // This scale has the UI zoom applied.
-  float ui_scale = display_.device_scale_factor();
-  float cursor_scale = cursor_.image_scale_factor();
+  // Use the original device scale factor instead of the display's, which
+  // might have been adjusted for the UI scale.
+  const float original_scale = Shell::Get()
+                                   ->display_manager()
+                                   ->GetDisplayInfo(display_.id())
+                                   .device_scale_factor();
+  // And use the nearest resource scale factor.
+  float cursor_scale = ui::GetScaleForResourceScaleFactor(
+      ui::GetSupportedResourceScaleFactor(original_scale));
+
   gfx::ImageSkia image;
   gfx::Point hot_point_in_physical_pixels;
   if (cursor_.type() == ui::mojom::CursorType::kCustom) {
     const SkBitmap& bitmap = cursor_.custom_bitmap();
     if (bitmap.isNull())
       return;
-    image = gfx::ImageSkia::CreateFromBitmap(bitmap, cursor_scale);
+    image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
     hot_point_in_physical_pixels = cursor_.custom_hotspot();
   } else {
     int resource_id;
@@ -344,31 +349,27 @@
         *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
   }
 
-  // The accessibility large cursor size is specified in dips, with respect to
-  // the original device scale factor.
-  if (cursor_size_ == ui::CursorSize::kLarge) {
-    const float dsf = Shell::Get()
-                          ->display_manager()
-                          ->GetDisplayInfo(display_.id())
-                          .device_scale_factor();
-    cursor_scale = dsf;
-    if (large_cursor_size_in_dip_ != image.size().width()) {
-      const float rescale = static_cast<float>(large_cursor_size_in_dip_) /
-                            static_cast<float>(image.size().width());
-      cursor_scale *= rescale;
-      hot_point_in_physical_pixels =
-          gfx::ScaleToCeiledPoint(hot_point_in_physical_pixels, rescale);
-    }
+  gfx::ImageSkia resized = image;
+
+  // Rescale cursor size. This is used with the combination of accessibility
+  // large cursor. We don't need to care about the case where cursor
+  // compositing is disabled as we always use cursor compositing if
+  // accessibility large cursor is enabled.
+  if (cursor_size_ == ui::CursorSize::kLarge &&
+      large_cursor_size_in_dip_ != image.size().width()) {
+    float rescale = static_cast<float>(large_cursor_size_in_dip_) /
+                    static_cast<float>(image.size().width());
+    resized = gfx::ImageSkiaOperations::CreateResizedImage(
+        image, skia::ImageOperations::ResizeMethod::RESIZE_GOOD,
+        gfx::ScaleToCeiledSize(image.size(), rescale));
+    hot_point_in_physical_pixels =
+        gfx::ScaleToCeiledPoint(hot_point_in_physical_pixels, rescale);
   }
 
-  // Make sure the cursor image isn't scaled by creating an ImageSkia with the
-  // target size for the UI scale.
-  const gfx::Size ui_size =
-      ScaleToRoundedSize(image.size(), cursor_scale / ui_scale);
-  const gfx::ImageSkiaRep& image_rep = image.GetRepresentation(cursor_scale);
-  delegate_->SetCursorImage(
-      ui_size,
-      gfx::ImageSkia::CreateFromBitmap(GetAdjustedBitmap(image_rep), ui_scale));
+  const gfx::ImageSkiaRep& image_rep = resized.GetRepresentation(cursor_scale);
+  delegate_->SetCursorImage(resized.size(),
+                            gfx::ImageSkia::CreateFromBitmap(
+                                GetAdjustedBitmap(image_rep), cursor_scale));
   // TODO(danakj): Should this be rounded? Or kept as a floating point?
   hot_point_ = gfx::ToFlooredPoint(
       gfx::ConvertPointToDips(hot_point_in_physical_pixels, cursor_scale));
diff --git a/ash/display/cursor_window_controller_unittest.cc b/ash/display/cursor_window_controller_unittest.cc
index e43f66f..ec2a481 100644
--- a/ash/display/cursor_window_controller_unittest.cc
+++ b/ash/display/cursor_window_controller_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "ash/display/cursor_window_controller.h"
 
-#include <string>
-
 #include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/constants/ash_constants.h"
 #include "ash/constants/ash_pref_names.h"
@@ -15,25 +13,16 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/command_line.h"
-#include "base/strings/stringprintf.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
-#include "ui/base/cursor/cursor_lookup.h"
-#include "ui/base/cursor/cursor_size.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
-#include "ui/base/layout.h"
-#include "ui/base/resource/scale_factor.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/color_utils.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/image/image_skia_rep.h"
-#include "ui/gfx/skia_util.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
@@ -75,15 +64,6 @@
     return cursor_window_controller_->display_.id();
   }
 
-  void SetCursorSize(ui::CursorSize size) {
-    cursor_window_controller_->SetCursorSize(size);
-  }
-
-  void SetLargeCursorSizeInDip(int large_cursor_size_in_dip) {
-    cursor_window_controller_->SetLargeCursorSizeInDip(
-        large_cursor_size_in_dip);
-  }
-
   void SetCursorCompositionEnabled(bool enabled) {
     // Cursor compositing will be enabled when high contrast mode is turned on.
     // Cursor compositing will be disabled when high contrast mode is the only
@@ -189,50 +169,25 @@
   EXPECT_TRUE(GetCursorWindow()->IsVisible());
 }
 
-// Test that the composited cursor matches the native cursor in size.
-TEST_F(CursorWindowControllerTest, CursorSize) {
-  for (int dsf : {1, 2}) {
-    std::string display_specs = base::StringPrintf("1000x500*%d", dsf);
-    UpdateDisplay(display_specs);
-    int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
-    display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
-                                                           primary_id);
-    for (float zoom_factor : {0.5f, 1.0f, 1.5f, 2.0f}) {
-      display_manager()->UpdateZoomFactor(primary_id, zoom_factor);
-      auto display = display::Screen::GetScreen()->GetPrimaryDisplay();
-      const float ui_scale = display.device_scale_factor();
+// Make sure that composition cursor stays big even when
+// the DSF becomes 1x as a result of zooming out.
+TEST_F(CursorWindowControllerTest, DSF) {
+  UpdateDisplay("1000x500*2");
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
 
-      ui::Cursor cursor(ui::mojom::CursorType::kPointer);
-      // This would be done by NativeCursorManagerAsh::SetCursor().
-      cursor.set_image_scale_factor(ui::GetScaleForResourceScaleFactor(
-          ui::GetSupportedResourceScaleFactor(ui_scale)));
+  display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
+                                                         primary_id);
+  SetCursorCompositionEnabled(true);
+  ASSERT_EQ(
+      2.0f,
+      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
+  EXPECT_TRUE(GetCursorImage().HasRepresentation(2.0f));
 
-      // Normal size.
-      SetCursorSize(ui::CursorSize::kNormal);
-
-      // Both checks are needed. To ensure that the cursor isn't scaled, besides
-      // checking that its size isn't changed, the image needs to be available
-      // for the current display scale.
-      EXPECT_EQ(GetCursorImage().GetRepresentation(ui_scale).pixel_size(),
-                gfx::SkISizeToSize(ui::GetCursorBitmap(cursor).dimensions()));
-      EXPECT_TRUE(GetCursorImage().HasRepresentation(ui_scale));
-
-      // Large size.
-      SetCursorSize(ui::CursorSize::kLarge);
-      for (const auto kLargeSizeInDips :
-           {kDefaultLargeCursorSize / 2, kDefaultLargeCursorSize}) {
-        SetLargeCursorSizeInDip(kLargeSizeInDips);
-        EXPECT_EQ(
-            GetCursorImage().GetRepresentation(ui_scale).pixel_size().width(),
-            kLargeSizeInDips * dsf);
-        EXPECT_TRUE(GetCursorImage().HasRepresentation(ui_scale));
-      }
-
-      // TODO(https://crbug.com/1149906): test custom cursors when
-      // WebCursor::GetNativeCursor() is moved to CursorLoader, as ash cannot
-      // depend on //content.
-    }
-  }
+  display_manager()->UpdateZoomFactor(primary_id, 0.5f);
+  ASSERT_EQ(
+      1.0f,
+      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
+  EXPECT_TRUE(GetCursorImage().HasRepresentation(2.0f));
 }
 
 // Test that cursor compositing is enabled if at least one of the features that
diff --git a/ash/quick_answers/ui/quick_answers_view.cc b/ash/quick_answers/ui/quick_answers_view.cc
index 6d81e58..2c68a63c 100644
--- a/ash/quick_answers/ui/quick_answers_view.cc
+++ b/ash/quick_answers/ui/quick_answers_view.cc
@@ -87,13 +87,11 @@
 constexpr int kSettingsButtonMarginDip = 8;
 constexpr int kSettingsButtonSizeDip = 14;
 constexpr SkColor kSettingsButtonColor = gfx::kGoogleGrey500;
-constexpr SkColor kSettingsButtonInkDropColor = gfx::kGoogleGrey500;
 
 // Phonetics audio button.
 constexpr int kPhoneticsAudioButtonMarginDip = 4;
 constexpr int kPhoneticsAudioButtonSizeDip = 14;
 constexpr SkColor kPhoneticsAudioButtonColor = gfx::kGoogleBlue600;
-constexpr SkColor kPhoneticsAudioButtonInkDropColor = gfx::kGoogleGrey500;
 
 // ReportQueryView.
 constexpr char kGoogleSansFont[] = "Google Sans";
@@ -481,12 +479,6 @@
                             kSettingsButtonColor));
   settings_button_->SetTooltipText(l10n_util::GetStringUTF16(
       IDS_ASH_QUICK_ANSWERS_SETTINGS_BUTTON_TOOLTIP_TEXT));
-
-  views::InkDropHost* const ink_drop = views::InkDrop::Get(settings_button_);
-  ink_drop->SetBaseColor(kSettingsButtonInkDropColor);
-  ink_drop->SetMode(views::InkDropHost::InkDropMode::ON);
-  settings_button_->SetHasInkDropActionOnClick(true);
-  views::InstallCircleHighlightPathGenerator(settings_button_);
 }
 
 void QuickAnswersView::AddPhoneticsAudioButton(const GURL& phonetics_audio,
@@ -515,13 +507,6 @@
       gfx::CreateVectorIcon(kSystemMenuVolumeHighIcon,
                             kPhoneticsAudioButtonSizeDip,
                             kPhoneticsAudioButtonColor));
-
-  views::InkDropHost* const ink_drop =
-      views::InkDrop::Get(phonetics_audio_button_);
-  ink_drop->SetBaseColor(kPhoneticsAudioButtonInkDropColor);
-  ink_drop->SetMode(views::InkDropHost::InkDropMode::ON);
-  phonetics_audio_button_->SetHasInkDropActionOnClick(true);
-  views::InstallCircleHighlightPathGenerator(phonetics_audio_button_);
 }
 
 void QuickAnswersView::AddAssistantIcon() {
@@ -658,8 +643,14 @@
   // retry-label, and so is not included when this is the case.
   if (!retry_label_)
     focusable_views.push_back(this);
+  if (settings_button_ && settings_button_->GetVisible())
+    focusable_views.push_back(settings_button_);
+  if (phonetics_audio_button_ && phonetics_audio_button_->GetVisible())
+    focusable_views.push_back(phonetics_audio_button_);
   if (retry_label_ && retry_label_->GetVisible())
     focusable_views.push_back(retry_label_);
+  if (report_query_view_ && report_query_view_->GetVisible())
+    focusable_views.push_back(report_query_view_);
   if (dogfood_button_ && dogfood_button_->GetVisible())
     focusable_views.push_back(dogfood_button_);
   return focusable_views;
diff --git a/ash/services/recording/public/mojom/BUILD.gn b/ash/services/recording/public/mojom/BUILD.gn
index 9962692..eca8eead 100644
--- a/ash/services/recording/public/mojom/BUILD.gn
+++ b/ash/services/recording/public/mojom/BUILD.gn
@@ -7,6 +7,8 @@
 mojom("mojom") {
   sources = [ "recording_service.mojom" ]
 
+  public_deps = [ "//sandbox/policy/mojom" ]
+
   deps = [
     "//media/mojo/mojom",
     "//services/viz/privileged/mojom/compositing",
diff --git a/ash/services/recording/public/mojom/recording_service.mojom b/ash/services/recording/public/mojom/recording_service.mojom
index 4e968aaa..8fdc4e4 100644
--- a/ash/services/recording/public/mojom/recording_service.mojom
+++ b/ash/services/recording/public/mojom/recording_service.mojom
@@ -7,6 +7,7 @@
 import "media/mojo/mojom/audio_stream_factory.mojom";
 import "mojo/public/mojom/base/big_string.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 import "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom";
 import "services/viz/public/mojom/compositing/frame_sink_id.mojom";
 import "services/viz/public/mojom/compositing/subtree_capture_id.mojom";
@@ -58,6 +59,8 @@
 // video frames, and writes the WebM muxed video chunks to directly to a file
 // whose path was given to the Record*() functions.
 // Note that a maximum of one screen recording can be done at any time.
+// TODO(https://crbug.com/1147991) Explore alternative sandboxing.
+[ServiceSandbox=sandbox.mojom.Sandbox.kVideoCapture]
 interface RecordingService {
   // All the below Record*() interfaces, take a pending remote to a client (e.g.
   // Ash) which will be notified when recording finishes and all the webm
diff --git a/ash/webui/scanning/mojom/scanning.mojom b/ash/webui/scanning/mojom/scanning.mojom
index aabc8ca..fb037c1 100644
--- a/ash/webui/scanning/mojom/scanning.mojom
+++ b/ash/webui/scanning/mojom/scanning.mojom
@@ -187,7 +187,11 @@
   // Scans the next page in a multi-page scan session. |success| indicates
   // whether the scan started successfully.
   ScanNextPage(mojo_base.mojom.UnguessableToken scanner_id,
-      ScanSettings settings)  => (bool success);
+      ScanSettings settings) => (bool success);
+
+  // Removes a scanned image from an ongoing multi-page scan session at the
+  // specified |page_index|. |page_index| is zero-based.
+  RemovePage(uint32 page_index);
 
   // Ends a multi-page scan session and saves the scan to disk.
   CompleteMultiPageScan();
diff --git a/base/BUILD.gn b/base/BUILD.gn
index ea830bff..7f1d68d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2616,6 +2616,7 @@
     "task/thread_pool/thread_pool_perftest.cc",
     "threading/counter_perftest.cc",
     "threading/thread_local_storage_perftest.cc",
+    "vlog_perftest.cc",
 
     # "test/run_all_unittests.cc",
     "json/json_perftest.cc",
diff --git a/base/barrier_callback.h b/base/barrier_callback.h
index 37ae6f0..aa898ef9 100644
--- a/base/barrier_callback.h
+++ b/base/barrier_callback.h
@@ -12,17 +12,18 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/synchronization/lock.h"
+#include "base/template_util.h"
 #include "base/thread_annotations.h"
 
 namespace base {
 
 namespace internal {
 
-template <typename T>
+template <typename T, typename DoneArg>
 class BarrierCallbackInfo {
  public:
   BarrierCallbackInfo(size_t num_callbacks,
-                      OnceCallback<void(std::vector<T>)> done_callback)
+                      OnceCallback<void(DoneArg)> done_callback)
       : num_callbacks_left_(num_callbacks),
         done_callback_(std::move(done_callback)) {
     results_.reserve(num_callbacks);
@@ -35,7 +36,7 @@
     --num_callbacks_left_;
 
     if (num_callbacks_left_ == 0) {
-      std::vector<T> results = std::move(results_);
+      std::vector<base::remove_cvref_t<T>> results = std::move(results_);
       lock.Release();
       std::move(done_callback_).Run(std::move(results));
     }
@@ -44,8 +45,8 @@
  private:
   Lock mutex_;
   size_t num_callbacks_left_ GUARDED_BY(mutex_);
-  std::vector<T> results_ GUARDED_BY(mutex_);
-  OnceCallback<void(std::vector<T>)> done_callback_;
+  std::vector<base::remove_cvref_t<T>> results_ GUARDED_BY(mutex_);
+  OnceCallback<void(DoneArg)> done_callback_;
 };
 
 template <typename T>
@@ -61,8 +62,16 @@
 // the vector of `T`s as an argument. (The ordering of the vector is
 // unspecified.)
 //
+// `T`s that are movable are moved into the callback's storage; otherwise the T
+// is copied. (BarrierCallback does not support `T`s that are neither movable
+// nor copyable.) If T is a reference, the reference is removed, and the
+// callback moves or copies the underlying value per the previously stated rule.
+//
 // If `num_callbacks` is 0, `done_callback` is executed immediately.
 //
+// `done_callback` may accept a `std::vector<T>`, `const std::vector<T>`, or
+// `const std::vector<T>&`.
+//
 // BarrierCallback is thread-safe - the internals are protected by a
 // `base::Lock`. `done_callback` will be run on the thread that calls the final
 // Run() on the returned callbacks, or the thread that constructed the
@@ -70,18 +79,27 @@
 //
 // `done_callback` is also cleared on the thread that runs it (by virtue of
 // being a OnceCallback).
-template <typename T>
+template <typename T,
+          typename RawArg = base::remove_cvref_t<T>,
+          typename DoneArg = std::vector<RawArg>,
+          template <typename>
+          class CallbackType,
+          typename std::enable_if<std::is_same<
+              std::vector<RawArg>,
+              base::remove_cvref_t<DoneArg>>::value>::type* = nullptr,
+          typename = base::EnableIfIsBaseCallback<CallbackType>>
 RepeatingCallback<void(T)> BarrierCallback(
     size_t num_callbacks,
-    OnceCallback<void(std::vector<T>)> done_callback) {
+    CallbackType<void(DoneArg)> done_callback) {
   if (num_callbacks == 0) {
     std::move(done_callback).Run({});
     return BindRepeating(&internal::ShouldNeverRun<T>);
   }
 
-  return BindRepeating(&internal::BarrierCallbackInfo<T>::Run,
-                       std::make_unique<internal::BarrierCallbackInfo<T>>(
-                           num_callbacks, std::move(done_callback)));
+  return BindRepeating(
+      &internal::BarrierCallbackInfo<T, DoneArg>::Run,
+      std::make_unique<internal::BarrierCallbackInfo<T, DoneArg>>(
+          num_callbacks, std::move(done_callback)));
 }
 
 }  // namespace base
diff --git a/base/barrier_callback_unittest.cc b/base/barrier_callback_unittest.cc
index 32f5342c..055b4e85 100644
--- a/base/barrier_callback_unittest.cc
+++ b/base/barrier_callback_unittest.cc
@@ -25,7 +25,8 @@
 }
 
 TEST(BarrierCallbackTest, ErrorToCallCallbackWithZeroCallbacks) {
-  auto barrier_callback = base::BarrierCallback<int>(0, base::DoNothing());
+  auto barrier_callback =
+      base::BarrierCallback<int>(0, base::DoNothing::Once<std::vector<int>>());
   EXPECT_FALSE(barrier_callback.is_null());
 
   EXPECT_CHECK_DEATH(barrier_callback.Run(3));
@@ -68,7 +69,7 @@
 
 TEST(BarrierCallbackTest, ReleasesDoneCallbackWhenDone) {
   bool done_destructed = false;
-  auto barrier_callback = base::BarrierCallback(
+  auto barrier_callback = base::BarrierCallback<bool>(
       1,
       base::BindOnce(&DestructionIndicator<std::vector<bool>>::DoNothing,
                      std::make_unique<DestructionIndicator<std::vector<bool>>>(
@@ -107,8 +108,37 @@
 
   // No need to assert anything here, since if BarrierCallback didn't work with
   // move-only types, this wouldn't compile.
-  auto barrier_callback = base::BarrierCallback<MoveOnly>(1, base::DoNothing());
+  auto barrier_callback = base::BarrierCallback<MoveOnly>(
+      1, base::DoNothing::Once<std::vector<MoveOnly>>());
   barrier_callback.Run(MoveOnly());
+
+  auto barrier_callback2 = base::BarrierCallback<MoveOnly>(
+      1, base::DoNothing::Once<const std::vector<MoveOnly>&>());
+  barrier_callback2.Run(MoveOnly());
+}
+
+TEST(BarrierCallbackTest, SupportsConstRefResults) {
+  auto barrier_callback = base::BarrierCallback<int>(
+      1, base::DoNothing::Once<const std::vector<int>&>());
+
+  barrier_callback.Run(1);
+}
+
+TEST(BarrierCallbackTest, SupportsReferenceTypes) {
+  class Referenceable {
+    // Must be copyable.
+  };
+  Referenceable ref;
+
+  // No need to assert anything here, since if BarrierCallback didn't work with
+  // by-reference args, this wouldn't compile.
+  auto barrier_callback = base::BarrierCallback<const Referenceable&>(
+      1, base::DoNothing::Once<std::vector<Referenceable>>());
+  barrier_callback.Run(ref);
+
+  auto barrier_callback2 = base::BarrierCallback<const Referenceable&>(
+      1, base::DoNothing::Once<const std::vector<Referenceable>&>());
+  barrier_callback2.Run(ref);
 }
 
 }  // namespace
diff --git a/base/vlog.cc b/base/vlog.cc
index cdc30c3..a973b317 100644
--- a/base/vlog.cc
+++ b/base/vlog.cc
@@ -5,11 +5,11 @@
 #include "base/vlog.h"
 
 #include <stddef.h>
-
+#include <algorithm>
+#include <limits>
 #include <ostream>
 #include <utility>
 
-#include "base/cxx17_backports.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -24,17 +24,16 @@
 
   explicit VmodulePattern(const std::string& pattern);
 
-  VmodulePattern();
+  VmodulePattern() = default;
 
   std::string pattern;
-  int vlog_level;
-  MatchTarget match_target;
+  int vlog_level = VlogInfo::kDefaultVlogLevel;
+  MatchTarget match_target = MATCH_MODULE;
+  size_t score = 0;
 };
 
 VlogInfo::VmodulePattern::VmodulePattern(const std::string& pattern)
-    : pattern(pattern),
-      vlog_level(VlogInfo::kDefaultVlogLevel),
-      match_target(MATCH_MODULE) {
+    : pattern(pattern) {
   // If the pattern contains a {forward,back} slash, we assume that
   // it's meant to be tested against the entire __FILE__ string.
   std::string::size_type first_slash = pattern.find_first_of("\\/");
@@ -42,10 +41,6 @@
     match_target = MATCH_FILE;
 }
 
-VlogInfo::VmodulePattern::VmodulePattern()
-    : vlog_level(VlogInfo::kDefaultVlogLevel),
-      match_target(MATCH_MODULE) {}
-
 VlogInfo::VlogInfo(const std::string& v_switch,
                    const std::string& vmodule_switch,
                    int* min_log_level)
@@ -86,31 +81,59 @@
 // Given a path, returns the basename with the extension chopped off
 // (and any -inl suffix).  We avoid using FilePath to minimize the
 // number of dependencies the logging system has.
-base::StringPiece GetModule(const base::StringPiece& file) {
-  base::StringPiece module(file);
-  base::StringPiece::size_type last_slash_pos =
-      module.find_last_of("\\/");
-  if (last_slash_pos != base::StringPiece::npos)
-    module.remove_prefix(last_slash_pos + 1);
+base::StringPiece GetModule(base::StringPiece file) {
+  base::StringPiece module = file;
+
+  // Chop off the file extension.
   base::StringPiece::size_type extension_start = module.rfind('.');
   module = module.substr(0, extension_start);
-  static const char kInlSuffix[] = "-inl";
-  static const int kInlSuffixLen = base::size(kInlSuffix) - 1;
+
+  // Chop off the -inl suffix.
+  static constexpr base::StringPiece kInlSuffix("-inl");
   if (base::EndsWith(module, kInlSuffix))
-    module.remove_suffix(kInlSuffixLen);
+    module.remove_suffix(kInlSuffix.size());
+
+  // Chop off the path up to the start of the file name. Using single-character
+  // overload of `base::StringPiece::find_last_of` for speed; this overload does
+  // not build a lookup table.
+  base::StringPiece::size_type last_slash_pos = module.find_last_of('/');
+  if (last_slash_pos != base::StringPiece::npos) {
+    module.remove_prefix(last_slash_pos + 1);
+    return module;
+  }
+  last_slash_pos = module.find_last_of('\\');
+  if (last_slash_pos != base::StringPiece::npos)
+    module.remove_prefix(last_slash_pos + 1);
   return module;
 }
 
 }  // namespace
 
-int VlogInfo::GetVlogLevel(const base::StringPiece& file) const {
+int VlogInfo::GetVlogLevel(base::StringPiece file) {
+  base::AutoLock lock(vmodule_levels_lock_);
   if (!vmodule_levels_.empty()) {
     base::StringPiece module(GetModule(file));
-    for (const auto& it : vmodule_levels_) {
-      base::StringPiece target(
-          (it.match_target == VmodulePattern::MATCH_FILE) ? file : module);
-      if (MatchVlogPattern(target, it.pattern))
-        return it.vlog_level;
+    for (size_t i = 0; i < vmodule_levels_.size(); i++) {
+      VmodulePattern& it = vmodule_levels_[i];
+
+      const bool kUseFile = it.match_target == VmodulePattern::MATCH_FILE;
+      if (!MatchVlogPattern(kUseFile ? file : module, it.pattern)) {
+        continue;
+      }
+      const int ret = it.vlog_level;
+
+      // Since `it` matched, increase its score because we believe it has a
+      // higher probability of winning next time.
+      if (it.score == std::numeric_limits<size_t>::max()) {
+        for (VmodulePattern& pattern : vmodule_levels_) {
+          pattern.score = 0;
+        }
+      }
+      ++it.score;
+      if (i > 0 && it.score > vmodule_levels_[i - 1].score)
+        std::swap(it, vmodule_levels_[i - 1]);
+
+      return ret;
     }
   }
   return GetMaxVlogLevel();
@@ -125,23 +148,20 @@
   return -*min_log_level_;
 }
 
-bool MatchVlogPattern(const base::StringPiece& string,
-                      const base::StringPiece& vlog_pattern) {
-  base::StringPiece pat(vlog_pattern);
-  base::StringPiece str(string);
-
+bool MatchVlogPattern(base::StringPiece string,
+                      base::StringPiece vlog_pattern) {
   // The code implements the glob matching using a greedy approach described in
   // https://research.swtch.com/glob.
   size_t s = 0, nexts = 0;
   size_t p = 0, nextp = 0;
-  size_t slen = str.size(), plen = pat.size();
+  const size_t slen = string.size(), plen = vlog_pattern.size();
   while (s < slen || p < plen) {
     if (p < plen) {
-      switch (pat[p]) {
+      switch (vlog_pattern[p]) {
         // A slash (forward or back) must match a slash (forward or back).
         case '/':
         case '\\':
-          if (s < slen && (str[s] == '/' || str[s] == '\\')) {
+          if (s < slen && (string[s] == '/' || string[s] == '\\')) {
             p++, s++;
             continue;
           }
@@ -160,7 +180,7 @@
           continue;
         // Anything else must match literally.
         default:
-          if (s < slen && str[s] == pat[p]) {
+          if (s < slen && string[s] == vlog_pattern[p]) {
             p++, s++;
             continue;
           }
diff --git a/base/vlog.h b/base/vlog.h
index 201daab..fd6d7397 100644
--- a/base/vlog.h
+++ b/base/vlog.h
@@ -10,6 +10,8 @@
 
 #include "base/base_export.h"
 #include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 
 namespace logging {
 
@@ -45,7 +47,7 @@
 
   // Returns the vlog level for a given file (usually taken from
   // __FILE__).
-  int GetVlogLevel(const base::StringPiece& file) const;
+  int GetVlogLevel(base::StringPiece file);
 
  private:
   void SetMaxVlogLevel(int level);
@@ -54,7 +56,8 @@
   // VmodulePattern holds all the information for each pattern parsed
   // from |vmodule_switch|.
   struct VmodulePattern;
-  std::vector<VmodulePattern> vmodule_levels_;
+  base::Lock vmodule_levels_lock_;
+  std::vector<VmodulePattern> vmodule_levels_ GUARDED_BY(vmodule_levels_lock_);
   int* min_log_level_;
 };
 
@@ -68,8 +71,8 @@
 //   "kh*n" matches "khn", "khan", or even "khaaaaan"
 //   "/foo\bar" matches "/foo/bar", "\foo\bar", or "/foo\bar"
 //     (disregarding C escaping rules)
-BASE_EXPORT bool MatchVlogPattern(const base::StringPiece& string,
-                                  const base::StringPiece& vlog_pattern);
+BASE_EXPORT bool MatchVlogPattern(base::StringPiece string,
+                                  base::StringPiece vlog_pattern);
 
 }  // namespace logging
 
diff --git a/base/vlog_perftest.cc b/base/vlog_perftest.cc
new file mode 100644
index 0000000..edabe1c
--- /dev/null
+++ b/base/vlog_perftest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/vlog.h"
+
+#include "base/logging.h"
+#include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
+
+namespace logging {
+
+TEST(VlogPerfTest, GetVlogLevel) {
+  const std::string kMetricThroughput = "throughput";
+  perf_test::PerfResultReporter reporter(/*metric_basename=*/"Vlog",
+                                         /*story_name=*/"GetVlogLevel");
+  reporter.RegisterImportantMetric(/*metric_suffix=*/kMetricThroughput,
+                                   /*units=*/"gets/microsecond");
+
+  const base::TimeTicks job_run_start(base::TimeTicks::Now());
+
+  const char kVModuleSwitch[] =
+      "foo/bar.cc=1,baz\\*\\qux.cc=2,*quux/*=3,*/*-inl.h=4";
+
+  constexpr size_t kNumFilenames = 3;
+  const char* const kFileNames[kNumFilenames] = {"baz/x/qux.cc", "foo/bar",
+                                                 "x/y-inl.h"};
+  int min_log_level = 0;
+  VlogInfo vlog_info(std::string(), kVModuleSwitch, &min_log_level);
+
+  constexpr size_t kNumSets = 1 << 21;
+  constexpr size_t kNumReps = 3;
+
+  for (size_t set = 0; set < kNumSets; set++) {
+    const char* kFileName = kFileNames[set % kNumFilenames];
+    for (size_t rep = 0; rep < kNumReps; rep++) {
+      int res = vlog_info.GetVlogLevel(kFileName);
+      ASSERT_GE(res, min_log_level);
+      ASSERT_LE(res, 4);
+    }
+  }
+
+  const base::TimeDelta kDuration = base::TimeTicks::Now() - job_run_start;
+  const int64_t kDurationUs = kDuration.InMicroseconds();
+  ASSERT_NE(kDurationUs, 0) << "Too fast, would divide by zero.";
+
+  base::CheckedNumeric<size_t> gets_per_us_checked = kNumSets;
+  gets_per_us_checked *= kNumReps;
+  gets_per_us_checked /= kDurationUs;
+
+  const size_t kGetsPerUs = gets_per_us_checked.ValueOrDie();
+  reporter.AddResult(kMetricThroughput, kGetsPerUs);
+}
+}  // namespace logging
diff --git a/build/config/android/BUILD.gn b/build/config/android/BUILD.gn
index b6b1b2d..3803fd4f 100644
--- a/build/config/android/BUILD.gn
+++ b/build/config/android/BUILD.gn
@@ -59,9 +59,6 @@
     cflags += [ "--rtlib=libgcc" ]
     if (arm_control_flow_integrity == "standard") {
       cflags += [ "-mbranch-protection=standard" ]
-    } else {
-      assert(arm_control_flow_integrity == "none",
-             "Invalid branch protection option")
     }
   }
 
diff --git a/build/config/arm.gni b/build/config/arm.gni
index 0473118c..0185dd73 100644
--- a/build/config/arm.gni
+++ b/build/config/arm.gni
@@ -125,8 +125,12 @@
   arm_use_neon = true
   declare_args() {
     # Enables the new Armv8 branch protection features. Valid strings are:
-    # - "standard": Enables both Pointer Authentication Code (featured in Armv8.3) and Branch Target Identification (featured in Armv8.5).
+    # - "standard": Enables both Pointer Authentication Code (featured in
+    #               Armv8.3) and Branch Target Identification (Armv8.5).
     # - "none": No branch protection.
     arm_control_flow_integrity = "none"
   }
+  assert(arm_control_flow_integrity == "none" ||
+             arm_control_flow_integrity == "standard",
+         "Invalid branch protection option")
 }
diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn
index 08471742..8e42cf9 100644
--- a/build/config/linux/BUILD.gn
+++ b/build/config/linux/BUILD.gn
@@ -22,9 +22,6 @@
     if (arm_control_flow_integrity == "standard") {
       cflags += [ "-mbranch-protection=standard" ]
       asmflags += [ "-mbranch-protection=standard" ]
-    } else {
-      assert(arm_control_flow_integrity == "none",
-             "Invalid branch protection option")
     }
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 59108a1..bca2f1f 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-6.20210810.0.1
+6.20210810.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 59108a1..bca2f1f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-6.20210810.0.1
+6.20210810.1.1
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index e4895ee..cf54a36 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1016,7 +1016,7 @@
       SetNeedsUpdateLayers();
     }
 
-    scroll_tree.NotifyDidScroll(id, new_offset, snap_target_ids);
+    scroll_tree.NotifyDidCompositorScroll(id, new_offset, snap_target_ids);
   } else if (Layer* layer = LayerByElementId(id)) {
     layer->SetScrollOffsetFromImplSide(layer->scroll_offset() + delta);
     SetNeedsUpdateLayers();
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 6cb642e..ee9668d 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -3579,10 +3579,10 @@
   void AfterTest() override { EXPECT_TRUE(sent_gesture_); }
 
   // ScrollCallbacks
-  void DidScroll(ElementId element_id,
-                 const gfx::ScrollOffset& scroll_offset,
-                 const absl::optional<TargetSnapAreaElementIds>&
-                     snap_target_ids) override {
+  void DidCompositorScroll(ElementId element_id,
+                           const gfx::ScrollOffset& scroll_offset,
+                           const absl::optional<TargetSnapAreaElementIds>&
+                               snap_target_ids) override {
     last_scrolled_element_id_ = element_id;
     last_scrolled_offset_ = scroll_offset;
   }
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index b0d0479..3a4ecfab 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -87,10 +87,10 @@
   }
 
   // ScrollCallbacks
-  void DidScroll(ElementId element_id,
-                 const gfx::ScrollOffset& scroll_offset,
-                 const absl::optional<TargetSnapAreaElementIds>&
-                     snap_target_ids) override {
+  void DidCompositorScroll(ElementId element_id,
+                           const gfx::ScrollOffset& scroll_offset,
+                           const absl::optional<TargetSnapAreaElementIds>&
+                               snap_target_ids) override {
     // Simulates cc client (e.g Blink) behavior when handling impl-side scrolls.
     SetScrollOffsetFromImplSide(layer_tree_host()->LayerByElementId(element_id),
                                 scroll_offset);
@@ -621,11 +621,12 @@
     }
   }
 
-  void DidScroll(ElementId element_id,
-                 const gfx::ScrollOffset& offset,
-                 const absl::optional<TargetSnapAreaElementIds>&
-                     snap_target_ids) override {
-    LayerTreeHostScrollTest::DidScroll(element_id, offset, snap_target_ids);
+  void DidCompositorScroll(ElementId element_id,
+                           const gfx::ScrollOffset& offset,
+                           const absl::optional<TargetSnapAreaElementIds>&
+                               snap_target_ids) override {
+    LayerTreeHostScrollTest::DidCompositorScroll(element_id, offset,
+                                                 snap_target_ids);
     if (element_id == expected_scroll_layer_->element_id()) {
       final_scroll_offset_ = CurrentScrollOffset(expected_scroll_layer_);
       EXPECT_EQ(offset, final_scroll_offset_);
@@ -1841,9 +1842,10 @@
     }
   }
 
-  void DidScroll(ElementId element_id,
-                 const gfx::ScrollOffset&,
-                 const absl::optional<TargetSnapAreaElementIds>&) override {
+  void DidCompositorScroll(
+      ElementId element_id,
+      const gfx::ScrollOffset&,
+      const absl::optional<TargetSnapAreaElementIds>&) override {
     if (scroll_destroy_whole_tree_) {
       layer_tree_host()->SetRootLayer(nullptr);
       layer_tree_host()->property_trees()->clear();
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 91436f3..0767972 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1720,13 +1720,15 @@
   callbacks_ = std::move(callbacks);
 }
 
-void ScrollTree::NotifyDidScroll(
+void ScrollTree::NotifyDidCompositorScroll(
     ElementId scroll_element_id,
     const gfx::ScrollOffset& scroll_offset,
     const absl::optional<TargetSnapAreaElementIds>& snap_target_ids) {
   DCHECK(property_trees()->is_main_thread);
-  if (callbacks_)
-    callbacks_->DidScroll(scroll_element_id, scroll_offset, snap_target_ids);
+  if (callbacks_) {
+    callbacks_->DidCompositorScroll(scroll_element_id, scroll_offset,
+                                    snap_target_ids);
+  }
 }
 
 void ScrollTree::NotifyDidChangeScrollbarsHidden(ElementId scroll_element_id,
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index e745252..3ac68f86 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -381,9 +381,10 @@
 class ScrollCallbacks {
  public:
   // Called after the composited scroll offset changed.
-  virtual void DidScroll(ElementId scroll_element_id,
-                         const gfx::ScrollOffset&,
-                         const absl::optional<TargetSnapAreaElementIds>&) = 0;
+  virtual void DidCompositorScroll(
+      ElementId scroll_element_id,
+      const gfx::ScrollOffset&,
+      const absl::optional<TargetSnapAreaElementIds>&) = 0;
   // Called after the hidden status of composited scrollbars changed. Note that
   // |scroll_element_id| is the element id of the scroll not of the scrollbars.
   virtual void DidChangeScrollbarsHidden(ElementId scroll_element_id,
@@ -500,7 +501,7 @@
 
   void SetScrollCallbacks(base::WeakPtr<ScrollCallbacks> callbacks);
 
-  void NotifyDidScroll(
+  void NotifyDidCompositorScroll(
       ElementId scroll_element_id,
       const gfx::ScrollOffset& scroll_offset,
       const absl::optional<TargetSnapAreaElementIds>& snap_target_ids);
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSwipeRefreshLayout.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSwipeRefreshLayout.java
index c1e47af9a..1ce58944 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSwipeRefreshLayout.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSwipeRefreshLayout.java
@@ -98,7 +98,9 @@
     public void showIPH(UserEducationHelper helper) {
         ViewGroup contentContainer = mActivity.findViewById(android.R.id.content);
         if (contentContainer == null) return;
-        View toolbarView = contentContainer.findViewById(org.chromium.chrome.R.id.toolbar);
+        // Only toolbar_container view appears in both NTP and start surface.
+        View toolbarView =
+                contentContainer.findViewById(org.chromium.chrome.R.id.toolbar_container);
         if (toolbarView == null) return;
         helper.requestShowIPH(new IPHCommandBuilder(getContext().getResources(),
                 FeatureConstants.FEED_SWIPE_REFRESH_FEATURE, R.string.feed_swipe_refresh_iph,
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListener.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListener.java
index 7d99577..0f01be17 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListener.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListener.java
@@ -51,7 +51,9 @@
     }
 
     @Override
-    public void onHeaderOffsetChanged(int verticalOffset) {}
+    public void onHeaderOffsetChanged(int verticalOffset) {
+        maybeTriggerIPH();
+    }
 
     private void maybeTriggerIPH() {
         final String featureForIph = FeatureConstants.FEED_SWIPE_REFRESH_FEATURE;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 772641d0..73b145b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -47,8 +47,6 @@
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
-import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
-import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.share.ShareUtils;
@@ -375,9 +373,7 @@
      *         is present for restoration.
      */
     private int getInstanceCount() {
-        return SharedPreferencesManager.getInstance()
-                .readIntsWithPrefix(ChromePreferenceKeys.MULTI_INSTANCE_URL)
-                .size();
+        return mMultiWindowModeStateDispatcher.getInstanceCount();
     }
 
     private void prepareCommonMenuItems(Menu menu, @MenuGroup int menuGroup, boolean isIncognito) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
index 6fe2cd24..8fb16f64 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
@@ -49,7 +49,8 @@
 
 class MultiInstanceManagerApi31 extends MultiInstanceManager {
     public static final int INVALID_INSTANCE_ID = MultiWindowUtils.INVALID_INSTANCE_ID;
-    public static final int INVALID_TASK_ID = -1; // Defined in android.app.ActivityTaskManager.
+    public static final int INVALID_TASK_ID = MultiWindowUtils.INVALID_TASK_ID;
+
     private static final String EMPTY_DATA = "";
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -87,14 +88,17 @@
 
     @Override
     public boolean handleMenuOrKeyboardAction(int id, boolean fromMenu) {
+        // clang-format off
         if (id == org.chromium.chrome.R.id.manage_all_windows_menu_id) {
+            List<InstanceInfo> info = getInstanceInfo();
             InstanceSwitcherCoordinator.showDialog(mActivity, mModalDialogManagerSupplier.get(),
                     new LargeIconBridge(getProfile()),
-                    (item)
-                            -> openInstance(item.instanceId, item.taskId),
-                    (item) -> closeInstance(item.instanceId, item.taskId), getInstanceInfo());
+                    (item) -> openInstance(item.instanceId, item.taskId),
+                    (item) -> closeInstance(item.instanceId, item.taskId),
+                    this::openNewWindow, info.size() < MultiWindowUtils.getMaxInstances(), info);
             return true;
         }
+        // clang-format on
         return super.handleMenuOrKeyboardAction(id, fromMenu);
     }
 
@@ -157,7 +161,7 @@
 
     @Override
     public List<InstanceInfo> getInstanceInfo() {
-        removeInvalidEntriesFromTaskMap();
+        removeInvalidInstanceData();
         List<InstanceInfo> result = new ArrayList<>();
         int currentItemPos = -1;
         for (int i = 0; i < mMaxInstances; ++i) {
@@ -177,7 +181,6 @@
                 }
             }
 
-            // TODO: Remove unrecoverable incognito tab-only instance entries.
             int taskId = getTaskFromMap(i);
             result.add(new InstanceInfo(i, taskId, type, url, readTitle(i), readTabCount(i),
                     readIncognitoTabCount(i), readIncognitoSelected(i)));
@@ -198,7 +201,7 @@
 
     @Override
     public int allocInstanceId(int windowId, int taskId, boolean preferNew) {
-        removeInvalidEntriesFromTaskMap();
+        removeInvalidInstanceData();
 
         // Explicitly specified window ID should be preferred. This comes from user selecting
         // a certain instance on UI. This method would never be called if there were an instance
@@ -241,7 +244,7 @@
     @Override
     public void initialize(int instanceId, int taskId) {
         mInstanceId = instanceId;
-        SharedPreferencesManager.getInstance().writeInt(taskMapKey(instanceId), taskId);
+        updateTaskMap(instanceId, taskId);
         installTabModelObserver();
     }
 
@@ -291,7 +294,6 @@
         };
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     static int getTaskFromMap(int index) {
         return SharedPreferencesManager.getInstance().readInt(taskMapKey(index), INVALID_TASK_ID);
     }
@@ -300,8 +302,13 @@
         return ChromePreferenceKeys.MULTI_INSTANCE_TASK_MAP.createKey(String.valueOf(index));
     }
 
-    private void removeInvalidEntriesFromTaskMap() {
-        // Remove tasks that do not exist any more.
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void updateTaskMap(int instanceId, int taskId) {
+        SharedPreferencesManager.getInstance().writeInt(taskMapKey(instanceId), taskId);
+    }
+
+    private void removeInvalidInstanceData() {
+        // Remove tasks that do not exist any more from the task map
         Set<Integer> validTasks = getAllChromeTasks();
         Map<String, Integer> taskMap = SharedPreferencesManager.getInstance().readIntsWithPrefix(
                 ChromePreferenceKeys.MULTI_INSTANCE_TASK_MAP);
@@ -310,6 +317,13 @@
                 SharedPreferencesManager.getInstance().removeKey(entry.getKey());
             }
         }
+
+        // Remove persistent data for unrecoverable instances.
+        for (int i = 0; i < mMaxInstances; ++i) {
+            if (readUrl(i) != null && !MultiWindowUtils.isRestorableInstance(i)) {
+                removeInstanceInfo(i);
+            }
+        }
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -351,7 +365,13 @@
         return false;
     }
 
-    protected static void writeIncognitoSelected(int index, Tab tab) {
+    private static String incognitoSelectedKey(int index) {
+        return ChromePreferenceKeys.MULTI_INSTANCE_IS_INCOGNITO_SELECTED.createKey(
+                String.valueOf(index));
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void writeIncognitoSelected(int index, Tab tab) {
         SharedPreferencesManager.getInstance().writeBoolean(
                 incognitoSelectedKey(index), tab.isIncognito());
     }
@@ -366,26 +386,19 @@
         return ChromePreferenceKeys.MULTI_INSTANCE_URL.createKey(String.valueOf(index));
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     static String readUrl(int index) {
         return SharedPreferencesManager.getInstance().readString(urlKey(index), null);
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    protected static void writeUrl(int index, String url) {
+    static void writeUrl(int index, String url) {
         SharedPreferencesManager.getInstance().writeString(urlKey(index), url);
     }
 
-    protected static void writeUrl(int index, Tab tab) {
+    private static void writeUrl(int index, Tab tab) {
         assert !tab.isIncognito();
         writeUrl(index, tab.getOriginalUrl().getSpec());
     }
 
-    private static String incognitoSelectedKey(int index) {
-        return ChromePreferenceKeys.MULTI_INSTANCE_IS_INCOGNITO_SELECTED.createKey(
-                String.valueOf(index));
-    }
-
     private static String titleKey(int index) {
         return ChromePreferenceKeys.MULTI_INSTANCE_TITLE.createKey(String.valueOf(index));
     }
@@ -404,11 +417,11 @@
         SharedPreferencesManager.getInstance().writeString(titleKey(index), title);
     }
 
-    private static String tabCountKey(int index) {
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static String tabCountKey(int index) {
         return ChromePreferenceKeys.MULTI_INSTANCE_TAB_COUNT.createKey(String.valueOf(index));
     }
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     static int readTabCount(int index) {
         return SharedPreferencesManager.getInstance().readInt(tabCountKey(index));
     }
@@ -423,7 +436,7 @@
         return SharedPreferencesManager.getInstance().readInt(incognitoTabCountKey(index));
     }
 
-    private static void writeTabCount(int index, TabModelSelector selector) {
+    static void writeTabCount(int index, TabModelSelector selector) {
         SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
         int tabCount = selector.getModel(false).getCount();
         prefs.writeInt(tabCountKey(index), tabCount);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcher.java
index f3cc6575..0a666f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcher.java
@@ -89,4 +89,9 @@
      * @return The ActivityOptions needed to open the content in another display.
      */
     Bundle getOpenInOtherWindowActivityOptions();
+
+    /**
+     * @return The number of Chrome instances that can switch to or launch.
+     */
+    int getInstanceCount();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcherImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcherImpl.java
index f3d9ebe..3aa81eab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcherImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcherImpl.java
@@ -91,4 +91,9 @@
     public Bundle getOpenInOtherWindowActivityOptions() {
         return MultiWindowUtils.getOpenInOtherWindowActivityOptions(mActivity);
     }
+
+    @Override
+    public int getInstanceCount() {
+        return MultiWindowUtils.getInstanceCount();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
index c4c47a9..314065f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
@@ -57,6 +57,7 @@
  */
 public class MultiWindowUtils implements ActivityStateListener {
     public static final int INVALID_INSTANCE_ID = TabWindowManager.INVALID_WINDOW_INDEX;
+    public static final int INVALID_TASK_ID = -1; // Defined in android.app.ActivityTaskManager.
 
     private static MultiWindowUtils sInstance = new MultiWindowUtils();
 
@@ -305,6 +306,22 @@
         return Display.INVALID_DISPLAY;
     }
 
+    /**
+     * @return The number of Chrome instances that can switch to or launch.
+     */
+    public static int getInstanceCount() {
+        int count = 0;
+        for (int i = 0; i < getMaxInstances(); ++i) {
+            if (MultiInstanceManagerApi31.readUrl(i) != null && isRestorableInstance(i)) count++;
+        }
+        return count;
+    }
+
+    static boolean isRestorableInstance(int index) {
+        return MultiInstanceManagerApi31.readTabCount(index) != 0
+                || MultiInstanceManagerApi31.getTaskFromMap(index) != INVALID_TASK_ID;
+    }
+
     @Override
     public void onActivityStateChange(Activity activity, int newState) {
         if (newState == ActivityState.RESUMED && activity instanceof ChromeTabbedActivity) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
index 99b1f9e..3be9f86c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
@@ -668,9 +668,12 @@
         rBar1.put("title", "Selection Related 1");
         JSONObject rBar2 = new JSONObject();
         rBar2.put("title", "Selection Related 2");
+        JSONObject rBar3 = new JSONObject();
+        rBar3.put("title", "Selection Related 3");
         JSONArray selectionSearches = new JSONArray();
         selectionSearches.put(rBar1);
         selectionSearches.put(rBar2);
+        selectionSearches.put(rBar3);
         suggestions.put("selection", selectionSearches);
 
         ResolvedSearchTerm intelligenceWithRelatedSearches =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index c81d9112..3e3e97e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -3973,7 +3973,6 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     public void testRelatedSearchesItemNotSelected() throws Exception {
-        FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_PANEL);
         mPolicy.overrideAllowSendingPageUrlForTesting(true);
         FakeResolveSearch fakeSearch = simulateResolveSearch("intelligence");
         Assert.assertFalse("Related Searches should have been requested but were not!",
@@ -3994,7 +3993,6 @@
     @Feature({"ContextualSearch"})
     @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.O, message = "crbug.com/1182040")
     public void testRelatedSearchesItemSelected() throws Exception {
-        FeatureList.setTestFeatures(ENABLE_RELATED_SEARCHES_IN_PANEL);
         mFakeServer.reset();
         FakeResolveSearch fakeSearch = simulateResolveSearch("intelligence");
         ResolvedSearchTerm resolvedSearchTerm = fakeSearch.getResolvedSearchTerm();
@@ -4004,12 +4002,12 @@
         tapPeekingBarToExpandAndAssert();
 
         // Select a Related Searches suggestion.
-        RelatedSearchesControl relatedSearchesControl = mPanel.getRelatedSearchesInContentControl();
+        RelatedSearchesControl relatedSearchesControl = mPanel.getRelatedSearchesInBarControl();
         final int chipToSelect = 2;
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> relatedSearchesControl.selectChipForTest(chipToSelect));
         Assert.assertEquals("The Related Searches query was not shown in the Bar!",
-                "Related Search 3", mPanel.getSearchBarControl().getSearchTerm());
+                "Selection Related 3", mPanel.getSearchBarControl().getSearchTerm());
 
         // Collapse the panel back to the peeking state
         peekPanel();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
index 23fc7ae..ab169e43 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageNavigationTest.java
@@ -19,6 +19,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.homepage.HomepageTestRule;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -34,11 +35,14 @@
 public class NewTabPageNavigationTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+    @Rule
+    public HomepageTestRule mHomepageTestRule = new HomepageTestRule();
 
     private EmbeddedTestServer mTestServer;
 
     @Before
     public void setUp() {
+        mHomepageTestRule.useChromeNTPForTest();
         mActivityTestRule.startMainActivityWithURL(null);
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
index 35a1b05..9e2004f4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
@@ -629,9 +629,15 @@
                         activity, null, null, index);
         if (pair == null) return INVALID_INSTANCE_ID;
 
-        mMultiInstanceManager.createInstance(pair.first, activity);
-        mMultiInstanceManager.initialize(pair.first, activity.getTaskId());
-        return pair.first;
+        int instanceId = pair.first;
+        mMultiInstanceManager.createInstance(instanceId, activity);
+        mMultiInstanceManager.initialize(instanceId, activity.getTaskId());
+
+        // Store minimal data to get the instance recognized.
+        MultiInstanceManagerApi31.writeUrl(instanceId, "url" + instanceId);
+        SharedPreferencesManager.getInstance().writeInt(
+                MultiInstanceManagerApi31.tabCountKey(index), 1);
+        return instanceId;
     }
 
     // Assert that the given task is new, and not in the task map.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
index bfc595f..e0de1b4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.multiwindow;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 import android.content.Context;
@@ -12,14 +13,28 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 
 /**
  * Unit tests for {@link MultiWindowUtilsUnit}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class MultiWindowUtilsUnitTest {
+    private static final int INSTANCE_ID_0 = 0;
+    private static final int INSTANCE_ID_1 = 1;
+    private static final int INSTANCE_ID_2 = 2;
+    private static final int TASK_ID_5 = 5;
+    private static final int TASK_ID_6 = 6;
+    private static final int TASK_ID_7 = 7;
+    private static final String URL_1 = "url1";
+    private static final String URL_2 = "url2";
+    private static final String URL_3 = "url3";
+
     private MultiWindowUtils mUtils;
     private boolean mIsInMultiWindowMode;
     private boolean mIsInMultiDisplayMode;
@@ -27,8 +42,16 @@
     private boolean mIsAutosplitSupported;
     private boolean mCustomMultiWindowSupported;
 
+    @Mock
+    TabModelSelector mTabModelSelector;
+    @Mock
+    TabModel mNormalTabModel;
+    @Mock
+    TabModel mIncognitoTabModel;
+
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mUtils = new MultiWindowUtils() {
             @Override
             public boolean isInMultiWindowMode(Activity activity) {
@@ -97,4 +120,31 @@
                     openInOtherWindow, mUtils.isOpenInOtherWindowSupported(null));
         }
     }
+
+    @Test
+    public void testGetInstanceCount() {
+        when(mTabModelSelector.getModel(false)).thenReturn(mNormalTabModel);
+        when(mTabModelSelector.getModel(true)).thenReturn(mIncognitoTabModel);
+
+        // Instance with no tabs (ID_1) still counts as long as it is alive.
+        writeInstanceInfo(INSTANCE_ID_0, URL_1, /*tabCount=*/3, /*incognitoTabCount=*/2, TASK_ID_5);
+        writeInstanceInfo(INSTANCE_ID_1, URL_2, /*tabCount=*/0, /*incognitoTabCount=*/0, TASK_ID_6);
+        writeInstanceInfo(INSTANCE_ID_2, URL_3, /*tabCount=*/6, /*incognitoTabCount=*/2, TASK_ID_7);
+        assertEquals(3, MultiWindowUtils.getInstanceCount());
+
+        // Instance with no running task is not taken into account if there is no normal tab,
+        // regardless of the # of incognito tabs.
+        writeInstanceInfo(INSTANCE_ID_1, URL_2, /*tabCount=*/0, /*incognitoTabCount=*/2,
+                MultiWindowUtils.INVALID_TASK_ID);
+        assertEquals(2, MultiWindowUtils.getInstanceCount());
+    }
+
+    private void writeInstanceInfo(
+            int instanceId, String url, int tabCount, int incognitoTabCount, int taskId) {
+        MultiInstanceManagerApi31.writeUrl(instanceId, url);
+        when(mNormalTabModel.getCount()).thenReturn(tabCount);
+        when(mIncognitoTabModel.getCount()).thenReturn(incognitoTabCount);
+        MultiInstanceManagerApi31.writeTabCount(instanceId, mTabModelSelector);
+        MultiInstanceManagerApi31.updateTaskMap(instanceId, taskId);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
index 153cbef..65f7737b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -215,6 +215,8 @@
                         mBookmarkBridgeSupplier, mFeedLauncher, mDialogManager, mSnackbarManager));
         SharedPreferencesManager.getInstance().removeKeysWithPrefix(
                 ChromePreferenceKeys.MULTI_INSTANCE_URL);
+        SharedPreferencesManager.getInstance().removeKeysWithPrefix(
+                ChromePreferenceKeys.MULTI_INSTANCE_TAB_COUNT);
     }
 
     @Test
@@ -298,12 +300,14 @@
         mIsNewWindowMenuFeatureEnabled = true;
         doReturn(true).when(mTabbedAppMenuPropertiesDelegate).instanceSwitcherEnabled();
 
-        writeUrl(0, "https://url0");
+        createInstance(0, "https://url0");
 
         Menu menu = createMenuForMultiWindow();
         assertTrue(isMenuVisible(menu, R.id.new_window_menu_id));
 
-        for (int i = 0; i < MultiWindowUtils.getMaxInstances(); ++i) writeUrl(i, "https://url" + i);
+        for (int i = 0; i < MultiWindowUtils.getMaxInstances(); ++i) {
+            createInstance(i, "https://url" + i);
+        }
 
         Menu menu2 = createMenuForMultiWindow();
         assertFalse(isMenuVisible(menu2, R.id.new_window_menu_id));
@@ -315,14 +319,16 @@
         mIsNewWindowMenuFeatureEnabled = true;
         doReturn(true).when(mTabbedAppMenuPropertiesDelegate).instanceSwitcherEnabled();
 
-        writeUrl(0, "https://url0");
+        createInstance(0, "https://url0");
 
         Menu menu = createMenuForMultiWindow();
+        assertEquals(1, mMultiWindowModeStateDispatcher.getInstanceCount());
         assertFalse(isMenuVisible(menu, R.id.move_to_other_window_menu_id));
 
-        writeUrl(1, "https://url1");
+        createInstance(1, "https://url1");
 
         Menu menu2 = createMenuForMultiWindow();
+        assertEquals(2, mMultiWindowModeStateDispatcher.getInstanceCount());
         assertTrue(isMenuVisible(menu2, R.id.move_to_other_window_menu_id));
     }
 
@@ -332,12 +338,12 @@
         mIsNewWindowMenuFeatureEnabled = true;
         doReturn(true).when(mTabbedAppMenuPropertiesDelegate).instanceSwitcherEnabled();
 
-        writeUrl(0, "https://url0");
+        createInstance(0, "https://url0");
 
         Menu menu = createMenuForMultiWindow();
         assertFalse(isMenuVisible(menu, R.id.manage_all_windows_menu_id));
 
-        writeUrl(1, "https://url1");
+        createInstance(1, "https://url1");
 
         Menu menu2 = createMenuForMultiWindow();
         assertTrue(isMenuVisible(menu2, R.id.manage_all_windows_menu_id));
@@ -473,6 +479,9 @@
         doReturn(mIsNewWindowMenuFeatureEnabled)
                 .when(mTabbedAppMenuPropertiesDelegate)
                 .isNewWindowMenuFeatureEnabled();
+        doReturn(MultiWindowUtils.getInstanceCount())
+                .when(mMultiWindowModeStateDispatcher)
+                .getInstanceCount();
         Menu menu = createTestMenu();
         mTabbedAppMenuPropertiesDelegate.prepareMenu(menu, null);
         return menu;
@@ -602,11 +611,11 @@
         return items.toString();
     }
 
-    private static void writeUrl(int index, String url) {
-        SharedPreferencesManager.getInstance().writeString(urlKey(index), url);
-    }
-
-    private static String urlKey(int index) {
-        return ChromePreferenceKeys.MULTI_INSTANCE_URL.createKey(String.valueOf(index));
+    private static void createInstance(int index, String url) {
+        String urlKey = ChromePreferenceKeys.MULTI_INSTANCE_URL.createKey(String.valueOf(index));
+        SharedPreferencesManager.getInstance().writeString(urlKey, url);
+        String tabCountKey =
+                ChromePreferenceKeys.MULTI_INSTANCE_TAB_COUNT.createKey(String.valueOf(index));
+        SharedPreferencesManager.getInstance().writeInt(tabCountKey, 1);
     }
 }
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index e0c78d90..5ea568c926 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -39,6 +39,7 @@
   "+content/public/common",
   "+extensions/common/constants.h",
   "+headless/public",
+  "+media/base/media_switches.h",
   "+native_client/src/trusted/service_runtime/osx",
   "+pdf/pdf_ppapi.h",
   "+sandbox",
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 9c12fe0..73d78a39 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -185,6 +185,7 @@
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"  // nogncheck
 #include "chromeos/lacros/lacros_dbus_helper.h"
 #include "chromeos/lacros/lacros_service.h"
+#include "media/base/media_switches.h"
 #endif
 
 base::LazyInstance<ChromeContentGpuClient>::DestructorAtExit
@@ -563,6 +564,29 @@
                                     init_params->default_paths->downloads,
                                     drivefs);
     }
+    // This lives here rather than in ChromeBrowserMainExtraPartsLacros due to
+    // timing constraints. If we relocate it, then the flags aren't propagated
+    // to the GPU process.
+    if (init_params->build_flags.has_value()) {
+      for (auto flag : init_params->build_flags.value()) {
+        switch (flag) {
+          case crosapi::mojom::BuildFlag::kUnknown:
+            break;
+          case crosapi::mojom::BuildFlag::kEnablePlatformEncryptedHevc:
+            base::CommandLine::ForCurrentProcess()->AppendSwitch(
+                switches::kLacrosEnablePlatformEncryptedHevc);
+            break;
+          case crosapi::mojom::BuildFlag::kEnablePlatformHevc:
+            base::CommandLine::ForCurrentProcess()->AppendSwitch(
+                switches::kLacrosEnablePlatformHevc);
+            break;
+          case crosapi::mojom::BuildFlag::kUseChromeosProtectedMedia:
+            base::CommandLine::ForCurrentProcess()->AppendSwitch(
+                switches::kLacrosUseChromeosProtectedMedia);
+            break;
+        }
+      }
+    }
   }
 #endif
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 92d0fefe..70c62469 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9498,9 +9498,6 @@
         <message name="IDS_HISTORY_CLOSED_RESTORE_WINDOW_MAC" desc="The Mac menu item for restoring all the tabs of a recently closed window.">
           Restore All Tabs
         </message>
-        <message name="IDS_HISTORY_INCOGNITO_DISCLAIMER_MAC" desc="The Mac menu item for the Incognito disclaimer text.">
-          History isn’t saved in Incognito
-        </message>
         <!-- Window menu -->
         <message name="IDS_MINIMIZE_WINDOW_MAC"
                  desc="The Mac menu item for minimize the window menu."
diff --git a/chrome/app/generated_resources_grd/IDS_HISTORY_INCOGNITO_DISCLAIMER_MAC.png.sha1 b/chrome/app/generated_resources_grd/IDS_HISTORY_INCOGNITO_DISCLAIMER_MAC.png.sha1
deleted file mode 100644
index 0a688c0..0000000
--- a/chrome/app/generated_resources_grd/IDS_HISTORY_INCOGNITO_DISCLAIMER_MAC.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c19f7b46f8031b8d97f541b1d0bfa8fdb59ca89a
\ No newline at end of file
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index df48011..f5481d5 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -27,6 +27,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/posix/eintr_wrapper.h"
@@ -56,6 +57,7 @@
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/common/channel_info.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "chromeos/startup/startup_switches.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -77,6 +79,21 @@
 
 namespace {
 
+// The actual Lacros launch mode.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class LacrosLaunchMode {
+  // Indicates that Lacros is disabled.
+  kLacrosDisabled = 0,
+  // Indicates that Lacros and Ash are both enabled and accessible by the user.
+  kSideBySide = 1,
+  // Similar to kSideBySide but Lacros is the primary browser.
+  kLacrosPrimary = 2,
+  // Lacros is the only browser and Ash is disabled.
+  kLacrosOnly = 3,
+  kMaxValue = kLacrosOnly
+};
+
 using LaunchParamsFromBackground = BrowserManager::LaunchParamsFromBackground;
 
 // Pointer to the global instance of BrowserManager.
@@ -723,6 +740,9 @@
 void BrowserManager::OnSessionStateChanged() {
   DCHECK_EQ(state_, State::NOT_INITIALIZED);
 
+  // Perform the UMA recording for the current Lacros mode of operation.
+  RecordLacrosLaunchMode();
+
   // Wait for session to become active.
   auto* session_manager = session_manager::SessionManager::Get();
   if (session_manager->session_state() !=
@@ -871,4 +891,23 @@
   // TODO(https://crbug.com/1194187): Implement this.
 }
 
+void BrowserManager::RecordLacrosLaunchMode() {
+  LacrosLaunchMode lacros_mode;
+  if (!browser_util::IsAshWebBrowserEnabled(chrome::GetChannel())) {
+    // As Ash is disabled, Lacros is the only available browser.
+    lacros_mode = LacrosLaunchMode::kLacrosOnly;
+  } else if (browser_util::IsLacrosPrimaryBrowser()) {
+    // Lacros is the primary browser - but Ash is still available.
+    lacros_mode = LacrosLaunchMode::kLacrosPrimary;
+  } else if (browser_util::IsLacrosEnabled()) {
+    // If Lacros is enabled but not primary or the only browser, the
+    // side by side mode is active.
+    lacros_mode = LacrosLaunchMode::kSideBySide;
+  } else {
+    lacros_mode = LacrosLaunchMode::kLacrosDisabled;
+  }
+
+  UMA_HISTOGRAM_ENUMERATION("ChromeOS.Ash.Lacros.Launch.Mode", lacros_mode);
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/browser_manager.h b/chrome/browser/ash/crosapi/browser_manager.h
index ae57805a..14054431 100644
--- a/chrome/browser/ash/crosapi/browser_manager.h
+++ b/chrome/browser/ash/crosapi/browser_manager.h
@@ -196,6 +196,9 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(BrowserManagerTest, LacrosKeepAlive);
 
+  // Remember the launch mode of Lacros.
+  void RecordLacrosLaunchMode();
+
   // These ash features are allowed to request that Lacros stay running in the
   // background.
   enum class Feature {
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index 8b22dc3..5716a4d4 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -86,6 +86,7 @@
 #include "components/version_info/version_info.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "media/capture/mojom/video_capture.mojom.h"
+#include "media/media_buildflags.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
 #include "services/device/public/mojom/hid.mojom.h"
@@ -698,6 +699,21 @@
     }
   }
 
+  // Add any BUILDFLAGs we use to pass our per-platform/ build configuration to
+  // lacros for runtime handling instead.
+  std::vector<crosapi::mojom::BuildFlag> build_flags;
+#if BUILDFLAG(ENABLE_PLATFORM_ENCRYPTED_HEVC)
+  build_flags.emplace_back(
+      crosapi::mojom::BuildFlag::kEnablePlatformEncryptedHevc);
+#endif  // BUILDFLAG(ENABLE_PLATFORM_ENCRYPTED_HEVC)
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+  build_flags.emplace_back(crosapi::mojom::BuildFlag::kEnablePlatformHevc);
+#endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
+#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+  build_flags.emplace_back(
+      crosapi::mojom::BuildFlag::kUseChromeosProtectedMedia);
+#endif  // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+  params->build_flags = std::move(build_flags);
   return params;
 }
 
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
index 1709100..5157167 100644
--- a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
+++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
@@ -17,19 +17,18 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/message_center/message_center.h"
 
-namespace chromeos {
+namespace ash {
 
 EventRewriterDelegateImpl::EventRewriterDelegateImpl(
     wm::ActivationClient* activation_client)
     : EventRewriterDelegateImpl(
           activation_client,
-          std::make_unique<ash::DeprecationNotificationController>(
+          std::make_unique<DeprecationNotificationController>(
               message_center::MessageCenter::Get())) {}
 
 EventRewriterDelegateImpl::EventRewriterDelegateImpl(
     wm::ActivationClient* activation_client,
-    std::unique_ptr<ash::DeprecationNotificationController>
-        deprecation_controller)
+    std::unique_ptr<DeprecationNotificationController> deprecation_controller)
     : pref_service_for_testing_(nullptr),
       activation_client_(activation_client),
       deprecation_controller_(std::move(deprecation_controller)) {}
@@ -115,7 +114,7 @@
 
   aura::Window* active_window = activation_client_->GetActiveWindow();
   return active_window &&
-         active_window->GetProperty(ash::kSearchKeyAcceleratorReservedKey);
+         active_window->GetProperty(kSearchKeyAcceleratorReservedKey);
 }
 
 bool EventRewriterDelegateImpl::NotifyDeprecatedRightClickRewrite() {
@@ -138,4 +137,4 @@
   return profile ? profile->GetPrefs() : nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.h b/chrome/browser/ash/events/event_rewriter_delegate_impl.h
index 056adb7d..a33a434 100644
--- a/chrome/browser/ash/events/event_rewriter_delegate_impl.h
+++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.h
@@ -12,18 +12,15 @@
 class PrefService;
 
 namespace ash {
-class DeprecationNotificationController;
-}
 
-namespace chromeos {
+class DeprecationNotificationController;
 
 class EventRewriterDelegateImpl : public ui::EventRewriterChromeOS::Delegate {
  public:
   explicit EventRewriterDelegateImpl(wm::ActivationClient* activation_client);
-  EventRewriterDelegateImpl(
-      wm::ActivationClient* activation_client,
-      std::unique_ptr<ash::DeprecationNotificationController>
-          deprecation_controller);
+  EventRewriterDelegateImpl(wm::ActivationClient* activation_client,
+                            std::unique_ptr<DeprecationNotificationController>
+                                deprecation_controller);
   ~EventRewriterDelegateImpl() override;
 
   void set_pref_service_for_testing(const PrefService* pref_service) {
@@ -50,12 +47,16 @@
   wm::ActivationClient* activation_client_;
 
   // Handles showing notifications when deprecated event rewrites occur.
-  std::unique_ptr<ash::DeprecationNotificationController>
-      deprecation_controller_;
+  std::unique_ptr<DeprecationNotificationController> deprecation_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(EventRewriterDelegateImpl);
 };
 
+}  // namespace ash
+
+// TODO(https://crbug.com/1164001): remove when ChromeOS code migration is done.
+namespace chromeos {
+using ::ash::EventRewriterDelegateImpl;
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_ASH_EVENTS_EVENT_REWRITER_DELEGATE_IMPL_H_
diff --git a/chrome/browser/ash/events/event_rewriter_unittest.cc b/chrome/browser/ash/events/event_rewriter_unittest.cc
index acbac35..9f1c229 100644
--- a/chrome/browser/ash/events/event_rewriter_unittest.cc
+++ b/chrome/browser/ash/events/event_rewriter_unittest.cc
@@ -169,7 +169,7 @@
 
 }  // namespace
 
-namespace chromeos {
+namespace ash {
 
 class EventRewriterTest : public ChromeAshTestBase {
  public:
@@ -183,8 +183,7 @@
     input_method::InitializeForTesting(
         input_method_manager_mock_);  // pass ownership
     auto deprecation_controller =
-        std::make_unique<ash::DeprecationNotificationController>(
-            &message_center_);
+        std::make_unique<DeprecationNotificationController>(&message_center_);
     deprecation_controller_ = deprecation_controller.get();
     delegate_ = std::make_unique<EventRewriterDelegateImpl>(
         nullptr, std::move(deprecation_controller));
@@ -366,8 +365,7 @@
   std::unique_ptr<EventRewriterDelegateImpl> delegate_;
   chromeos::input_method::FakeImeKeyboard fake_ime_keyboard_;
   std::unique_ptr<ui::EventRewriterChromeOS> rewriter_;
-  ash::DeprecationNotificationController*
-      deprecation_controller_;  // Not owned.
+  DeprecationNotificationController* deprecation_controller_;  // Not owned.
   message_center::FakeMessageCenter message_center_;
 };
 
@@ -3812,7 +3810,7 @@
 
   void SetUp() override {
     ChromeAshTestBase::SetUp();
-    sticky_keys_controller_ = ash::Shell::Get()->sticky_keys_controller();
+    sticky_keys_controller_ = Shell::Get()->sticky_keys_controller();
     delegate_ = std::make_unique<EventRewriterDelegateImpl>(nullptr);
     delegate_->set_pref_service_for_testing(prefs());
     rewriter_ = std::make_unique<ui::EventRewriterChromeOS>(
@@ -3828,7 +3826,7 @@
   }
 
  protected:
-  ash::StickyKeysController* sticky_keys_controller_;
+  StickyKeysController* sticky_keys_controller_;
 
  private:
   std::unique_ptr<EventRewriterDelegateImpl> delegate_;
@@ -4025,7 +4023,7 @@
 
 TEST_F(EventRewriterTest, DontRewriteIfNotRewritten_SearchClickIsRightClick) {
   scoped_feature_list_.InitAndEnableFeature(
-      chromeos::features::kUseSearchClickForRightClick);
+      features::kUseSearchClickForRightClick);
   DontRewriteIfNotRewritten(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_COMMAND_DOWN);
   EXPECT_EQ(message_center_.NotificationCount(), 0);
 }
@@ -4392,34 +4390,34 @@
     ASSERT_TRUE(overlay_);
   }
 
-  ash::StickyKeysOverlay* overlay_;
+  StickyKeysOverlay* overlay_;
 };
 
 TEST_F(StickyKeysOverlayTest, OneModifierEnabled) {
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
 
   // Pressing modifier key should show overlay.
   SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
                                ui::DomKey::CONTROL);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
 
   // Pressing a normal key should hide overlay.
   SendActivateStickyKeyPattern(ui::VKEY_T, ui::DomCode::US_T,
                                ui::DomKey::Constant<'t'>::Character);
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
 }
 
 TEST_F(StickyKeysOverlayTest, TwoModifiersEnabled) {
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
 
   // Pressing two modifiers should show overlay.
@@ -4428,24 +4426,24 @@
   SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
                                ui::DomKey::CONTROL);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
 
   // Pressing a normal key should hide overlay.
   SendActivateStickyKeyPattern(ui::VKEY_N, ui::DomCode::US_N,
                                ui::DomKey::Constant<'n'>::Character);
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
 }
 
 TEST_F(StickyKeysOverlayTest, LockedModifier) {
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
 
   // Pressing a modifier key twice should lock modifier and show overlay.
@@ -4454,22 +4452,22 @@
   SendActivateStickyKeyPattern(ui::VKEY_LMENU, ui::DomCode::ALT_LEFT,
                                ui::DomKey::ALT);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
 
   // Pressing a normal key should not hide overlay.
   SendActivateStickyKeyPattern(ui::VKEY_D, ui::DomCode::US_D,
                                ui::DomKey::Constant<'d'>::Character);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
 }
 
 TEST_F(StickyKeysOverlayTest, LockedAndNormalModifier) {
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
 
   // Pressing a modifier key twice should lock modifier and show overlay.
@@ -4478,37 +4476,37 @@
   SendActivateStickyKeyPattern(ui::VKEY_CONTROL, ui::DomCode::CONTROL_LEFT,
                                ui::DomKey::CONTROL);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
 
   // Pressing another modifier key should still show overlay.
   SendActivateStickyKeyPattern(ui::VKEY_SHIFT, ui::DomCode::SHIFT_LEFT,
                                ui::DomKey::SHIFT);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
 
   // Pressing a normal key should not hide overlay but disable normal modifier.
   SendActivateStickyKeyPattern(ui::VKEY_D, ui::DomCode::US_D,
                                ui::DomKey::Constant<'d'>::Character);
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
 }
 
 TEST_F(StickyKeysOverlayTest, ModifiersDisabled) {
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_COMMAND_DOWN));
 
   // Enable modifiers.
@@ -4526,13 +4524,13 @@
                                ui::DomKey::META);
 
   EXPECT_TRUE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
             overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED,
+  EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
             overlay_->GetModifierKeyState(ui::EF_COMMAND_DOWN));
 
   // Disable modifiers and overlay should be hidden.
@@ -4550,13 +4548,13 @@
                                ui::DomKey::META);
 
   EXPECT_FALSE(overlay_->is_visible());
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
-  EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED,
+  EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
             overlay_->GetModifierKeyState(ui::EF_COMMAND_DOWN));
 }
 
@@ -4740,4 +4738,4 @@
         ui::DomKey::Constant<'1'>::Character}});
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/ash/input_method/ime_service_connector.cc b/chrome/browser/ash/input_method/ime_service_connector.cc
index 81be41b..3e1a9226 100644
--- a/chrome/browser/ash/input_method/ime_service_connector.cc
+++ b/chrome/browser/ash/input_method/ime_service_connector.cc
@@ -8,8 +8,8 @@
 #include <utility>
 
 #include "base/files/file_util.h"
-#include "chrome/browser/chromeos/service_sandbox_type.h"
 #include "chromeos/services/ime/constants.h"
+#include "chromeos/services/ime/public/mojom/ime_service.mojom.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "content/public/browser/service_process_host.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/ash/scanning/scan_service.cc b/chrome/browser/ash/scanning/scan_service.cc
index 1a3862d..1ab74286 100644
--- a/chrome/browser/ash/scanning/scan_service.cc
+++ b/chrome/browser/ash/scanning/scan_service.cc
@@ -296,6 +296,26 @@
                                      weak_ptr_factory_.GetWeakPtr())));
 }
 
+void ScanService::RemovePage(uint32_t page_index) {
+  // TODO: Update RemovePage() to allow removing with only one scanned image
+  // once the UI supports it.
+  if (scanned_images_.size() <= 1) {
+    mojo::ReportBadMessage(
+        "Invalid call to ScanService::RemovePage(), 1 or less scanned images "
+        "available");
+    return;
+  }
+
+  if (page_index >= scanned_images_.size()) {
+    mojo::ReportBadMessage(
+        "Invalid page_index passed to ScanService::RemovePage()");
+    return;
+  }
+
+  scanned_images_.erase(scanned_images_.begin() + page_index);
+  --num_pages_scanned_;
+}
+
 void ScanService::CompleteMultiPageScan() {
   OnScanCompleted(lorgnette::SCAN_FAILURE_MODE_NO_FAILURE);
   multi_page_controller_receiver_.reset();
diff --git a/chrome/browser/ash/scanning/scan_service.h b/chrome/browser/ash/scanning/scan_service.h
index 8d85a659..9f7252f 100644
--- a/chrome/browser/ash/scanning/scan_service.h
+++ b/chrome/browser/ash/scanning/scan_service.h
@@ -72,6 +72,7 @@
   void ScanNextPage(const base::UnguessableToken& scanner_id,
                     scanning::mojom::ScanSettingsPtr settings,
                     ScanNextPageCallback callback) override;
+  void RemovePage(uint32_t page_index) override;
   void CompleteMultiPageScan() override;
 
   // Binds receiver_ by consuming |pending_receiver|.
diff --git a/chrome/browser/ash/scanning/scan_service_unittest.cc b/chrome/browser/ash/scanning/scan_service_unittest.cc
index e2cddda..58e8b63f 100644
--- a/chrome/browser/ash/scanning/scan_service_unittest.cc
+++ b/chrome/browser/ash/scanning/scan_service_unittest.cc
@@ -366,6 +366,11 @@
     task_environment_.RunUntilIdle();
   }
 
+  void RemovePage(const int page_number) {
+    multi_page_scan_controller_remote_->RemovePage(page_number);
+    task_environment_.RunUntilIdle();
+  }
+
  protected:
   // A `BrowserTaskEnvironment` allows the test to create a `TestingProfile`.
   content::BrowserTaskEnvironment task_environment_{
@@ -881,4 +886,96 @@
   EXPECT_FALSE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
 }
 
+// Test that a page can be removed from a multi-page scan with two scanned
+// images.
+TEST_F(ScanServiceTest, MultiPageScanRemoveWithTwoPages) {
+  base::HistogramTester histogram_tester;
+  scoped_feature_list_.InitWithFeatures(
+      {chromeos::features::kScanAppMultiPageScan}, {});
+
+  fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
+      {kFirstTestScannerName});
+  const std::vector<std::string> scan_data = {CreatePng()};
+  fake_lorgnette_scanner_manager_.SetScanResponse(scan_data);
+  auto scanners = GetScanners();
+  ASSERT_EQ(scanners.size(), 1u);
+
+  mojo_ipc::ScanSettings settings = CreateScanSettings(
+      scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
+
+  // Scan two pages without completing the scan.
+  EXPECT_TRUE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
+  EXPECT_TRUE(ScanNextPage(scanners[0]->id, settings.Clone()));
+
+  // Delete the first page.
+  RemovePage(0);
+
+  CompleteMultiPageScan();
+
+  // Expect 1 record of the Scanning.NumPagesScanned metric in the 1 pages
+  // scanned bucket.
+  histogram_tester.ExpectUniqueSample("Scanning.NumPagesScanned", 1, 1);
+}
+
+// Test that a page can be removed from a multi-page scan with three scanned
+// images.
+TEST_F(ScanServiceTest, MultiPageScanRemoveWithThreePages) {
+  base::HistogramTester histogram_tester;
+  scoped_feature_list_.InitWithFeatures(
+      {chromeos::features::kScanAppMultiPageScan}, {});
+
+  fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
+      {kFirstTestScannerName});
+  const std::vector<std::string> scan_data = {CreatePng()};
+  fake_lorgnette_scanner_manager_.SetScanResponse(scan_data);
+  auto scanners = GetScanners();
+  ASSERT_EQ(scanners.size(), 1u);
+
+  mojo_ipc::ScanSettings settings = CreateScanSettings(
+      scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
+
+  // Scan three pages without completing the scan.
+  EXPECT_TRUE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
+  EXPECT_TRUE(ScanNextPage(scanners[0]->id, settings.Clone()));
+  EXPECT_TRUE(ScanNextPage(scanners[0]->id, settings.Clone()));
+
+  // Delete the second page.
+  RemovePage(1);
+
+  CompleteMultiPageScan();
+
+  // Expect 1 record of the Scanning.NumPagesScanned metric in the 2 pages
+  // scanned bucket.
+  histogram_tester.ExpectUniqueSample("Scanning.NumPagesScanned", 2, 1);
+}
+
+// Test that if there's only one page available, it can't be removed during a
+// multi-page scan session.
+TEST_F(ScanServiceTest, MultiPageScanCantRemoveOnePage) {
+  base::HistogramTester histogram_tester;
+  scoped_feature_list_.InitWithFeatures(
+      {chromeos::features::kScanAppMultiPageScan}, {});
+
+  fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
+      {kFirstTestScannerName});
+  const std::vector<std::string> scan_data = {CreatePng()};
+  fake_lorgnette_scanner_manager_.SetScanResponse(scan_data);
+  auto scanners = GetScanners();
+  ASSERT_EQ(scanners.size(), 1u);
+
+  mojo_ipc::ScanSettings settings = CreateScanSettings(
+      scanned_files_mount_->GetRootPath(), mojo_ipc::FileType::kPdf);
+
+  // Scan four pages without completing the scan.
+  EXPECT_TRUE(StartMultiPageScan(scanners[0]->id, settings.Clone()));
+
+  // The attempt to delete the only page should do nothing.
+  RemovePage(0);
+  CompleteMultiPageScan();
+
+  // Expect 1 record of the Scanning.NumPagesScanned metric in the 1 page
+  // scanned bucket.
+  histogram_tester.ExpectUniqueSample("Scanning.NumPagesScanned", 1, 1);
+}
+
 }  // namespace ash
diff --git a/chrome/browser/browsing_data/access_context_audit_service_factory.cc b/chrome/browser/browsing_data/access_context_audit_service_factory.cc
index 12a1aa3..fc5f0b9 100644
--- a/chrome/browser/browsing_data/access_context_audit_service_factory.cc
+++ b/chrome/browser/browsing_data/access_context_audit_service_factory.cc
@@ -47,8 +47,8 @@
   // cookies.
   DCHECK(profile->ShouldPersistSessionCookies());
 
-  std::unique_ptr<AccessContextAuditService> context_audit_service(
-      new AccessContextAuditService(profile));
+  auto context_audit_service =
+      std::make_unique<AccessContextAuditService>(profile);
   if (!context_audit_service->Init(
           context->GetPath(),
           context->GetDefaultStoragePartition()
diff --git a/chrome/browser/browsing_data/access_context_audit_service_unittest.cc b/chrome/browser/browsing_data/access_context_audit_service_unittest.cc
index b7a8c5b..5aedec0 100644
--- a/chrome/browser/browsing_data/access_context_audit_service_unittest.cc
+++ b/chrome/browser/browsing_data/access_context_audit_service_unittest.cc
@@ -98,8 +98,8 @@
 
   std::unique_ptr<KeyedService> BuildTestContextAuditService(
       content::BrowserContext* context) {
-    std::unique_ptr<AccessContextAuditService> service(
-        new AccessContextAuditService(static_cast<Profile*>(context)));
+    auto service = std::make_unique<AccessContextAuditService>(
+        static_cast<Profile*>(context));
     service->SetTaskRunnerForTesting(task_runner_);
     service->Init(temp_directory_.GetPath(), cookie_manager(),
                   history_service(), storage_partition());
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper.h b/chrome/browser/browsing_data/browsing_data_quota_helper.h
index 9a84c3a..4b63601 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper.h
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper.h
@@ -61,7 +61,7 @@
   using QuotaInfoArray = std::list<QuotaInfo>;
   using FetchResultCallback = base::OnceCallback<void(const QuotaInfoArray&)>;
 
-  static BrowsingDataQuotaHelper* Create(Profile* profile);
+  static scoped_refptr<BrowsingDataQuotaHelper> Create(Profile* profile);
 
   virtual void StartFetching(FetchResultCallback callback) = 0;
 
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
index 07fd1ac..9aab27e 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
@@ -29,8 +29,9 @@
 using content::BrowserContext;
 
 // static
-BrowsingDataQuotaHelper* BrowsingDataQuotaHelper::Create(Profile* profile) {
-  return new BrowsingDataQuotaHelperImpl(
+scoped_refptr<BrowsingDataQuotaHelper> BrowsingDataQuotaHelper::Create(
+    Profile* profile) {
+  return base::MakeRefCounted<BrowsingDataQuotaHelperImpl>(
       profile->GetDefaultStoragePartition()->GetQuotaManager());
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
index 6d648c1..e0f77695 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
@@ -13,8 +13,8 @@
 #include <utility>
 
 #include "base/callback_forward.h"
-#include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom-forward.h"
@@ -35,12 +35,17 @@
   void StartFetching(FetchResultCallback callback) override;
   void RevokeHostQuota(const std::string& host) override;
 
+  explicit BrowsingDataQuotaHelperImpl(storage::QuotaManager* quota_manager);
+
+  BrowsingDataQuotaHelperImpl(const BrowsingDataQuotaHelperImpl&) = delete;
+  BrowsingDataQuotaHelperImpl& operator=(const BrowsingDataQuotaHelperImpl&) =
+      delete;
+
  private:
   using PendingHosts =
       std::set<std::pair<std::string, blink::mojom::StorageType>>;
   using QuotaInfoMap = std::map<std::string, QuotaInfo>;
 
-  explicit BrowsingDataQuotaHelperImpl(storage::QuotaManager* quota_manager);
   ~BrowsingDataQuotaHelperImpl() override;
 
   // Calls QuotaManager::GetStorageKeysModifiedBetween for each storage type.
@@ -75,10 +80,6 @@
 
   base::WeakPtrFactory<BrowsingDataQuotaHelperImpl> weak_factory_{this};
 
-  friend class BrowsingDataQuotaHelper;
-  friend class BrowsingDataQuotaHelperTest;
-
-  DISALLOW_COPY_AND_ASSIGN(BrowsingDataQuotaHelperImpl);
 };
 
 #endif  // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_IMPL_H_
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
index 01e198b..a129255 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
@@ -43,8 +43,8 @@
         content::GetIOThreadTaskRunner({}).get(),
         /*quota_change_callback=*/base::DoNothing(),
         /*special_storage_policy=*/nullptr, storage::GetQuotaSettingsFunc());
-    helper_ = base::WrapRefCounted(
-        new BrowsingDataQuotaHelperImpl(quota_manager_.get()));
+    helper_ =
+        base::MakeRefCounted<BrowsingDataQuotaHelperImpl>(quota_manager_.get());
   }
 
   void TearDown() override {
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
index 859a894..43cb8c43 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
@@ -183,10 +183,9 @@
   // Start a download.
   content::DownloadManager* download_manager =
       GetBrowser()->profile()->GetDownloadManager();
-  std::unique_ptr<content::DownloadTestObserver> observer(
-      new content::DownloadTestObserverTerminal(
-          download_manager, 1,
-          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT));
+  auto observer = std::make_unique<content::DownloadTestObserverTerminal>(
+      download_manager, 1,
+      content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT);
 
   GURL download_url =
       ui_test_utils::GetTestUrl(base::FilePath().AppendASCII("downloads"),
@@ -363,23 +362,26 @@
   content::NativeIOContext* native_io_context =
       storage_partition->GetNativeIOContext();
   auto container = std::make_unique<LocalDataContainer>(
-      new browsing_data::CookieHelper(
+      base::MakeRefCounted<browsing_data::CookieHelper>(
           storage_partition,
           CookiesTreeModel::GetCookieDeletionDisabledCallback(profile)),
-      new browsing_data::DatabaseHelper(profile),
-      new browsing_data::LocalStorageHelper(profile),
+      base::MakeRefCounted<browsing_data::DatabaseHelper>(profile),
+      base::MakeRefCounted<browsing_data::LocalStorageHelper>(profile),
       /*session_storage_helper=*/nullptr,
-      new browsing_data::AppCacheHelper(
+      base::MakeRefCounted<browsing_data::AppCacheHelper>(
           storage_partition->GetAppCacheService()),
-      new browsing_data::IndexedDBHelper(storage_partition),
+      base::MakeRefCounted<browsing_data::IndexedDBHelper>(storage_partition),
       base::MakeRefCounted<browsing_data::FileSystemHelper>(
           file_system_context,
           browsing_data_file_system_util::GetAdditionalFileSystemTypes(),
           native_io_context),
       BrowsingDataQuotaHelper::Create(profile),
-      new browsing_data::ServiceWorkerHelper(service_worker_context),
-      new browsing_data::SharedWorkerHelper(storage_partition),
-      new browsing_data::CacheStorageHelper(storage_partition),
+      base::MakeRefCounted<browsing_data::ServiceWorkerHelper>(
+          service_worker_context),
+      base::MakeRefCounted<browsing_data::SharedWorkerHelper>(
+          storage_partition),
+      base::MakeRefCounted<browsing_data::CacheStorageHelper>(
+          storage_partition),
       BrowsingDataMediaLicenseHelper::Create(file_system_context));
   base::RunLoop run_loop;
   CookiesTreeObserver observer(run_loop.QuitClosure());
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index c2bfbfb..bee9618 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -281,7 +281,7 @@
     : profile_(Profile::FromBrowserContext(browser_context))
 #if defined(OS_ANDROID)
       ,
-      webapp_registry_(new WebappRegistry())
+      webapp_registry_(std::make_unique<WebappRegistry>())
 #endif
 {
   domain_reliability_clearer_ = base::BindRepeating(
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index df7145b8..522320974 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1132,9 +1132,10 @@
         FaviconServiceFactory::GetDefaultFactory());
     profile_builder.AddTestingFactory(
         SpellcheckServiceFactory::GetInstance(),
-        base::BindRepeating([](content::BrowserContext* profile) {
-          return std::unique_ptr<KeyedService>(
-              new SpellcheckService(static_cast<Profile*>(profile)));
+        base::BindRepeating([](content::BrowserContext* profile)
+                                -> std::unique_ptr<KeyedService> {
+          return std::make_unique<SpellcheckService>(
+              static_cast<Profile*>(profile));
         }));
     profile_builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
                                       SyncServiceFactory::GetDefaultFactory());
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index a5a824d..d7047043 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -1205,16 +1205,18 @@
 CookieTreeCookiesNode* CookieTreeHostNode::GetOrCreateCookiesNode() {
   if (cookies_child_)
     return cookies_child_;
-  cookies_child_ = new CookieTreeCookiesNode;
-  AddChildSortedByTitle(base::WrapUnique(cookies_child_));
+  auto cookies_node = std::make_unique<CookieTreeCookiesNode>();
+  cookies_child_ = cookies_node.get();
+  AddChildSortedByTitle(std::move(cookies_node));
   return cookies_child_;
 }
 
 CookieTreeDatabasesNode* CookieTreeHostNode::GetOrCreateDatabasesNode() {
   if (databases_child_)
     return databases_child_;
-  databases_child_ = new CookieTreeDatabasesNode;
-  AddChildSortedByTitle(base::WrapUnique(databases_child_));
+  auto databases_node = std::make_unique<CookieTreeDatabasesNode>();
+  databases_child_ = databases_node.get();
+  AddChildSortedByTitle(std::move(databases_node));
   return databases_child_;
 }
 
@@ -1222,8 +1224,9 @@
     CookieTreeHostNode::GetOrCreateLocalStoragesNode() {
   if (local_storages_child_)
     return local_storages_child_;
-  local_storages_child_ = new CookieTreeLocalStoragesNode;
-  AddChildSortedByTitle(base::WrapUnique(local_storages_child_));
+  auto local_storages_node = std::make_unique<CookieTreeLocalStoragesNode>();
+  local_storages_child_ = local_storages_node.get();
+  AddChildSortedByTitle(std::move(local_storages_node));
   return local_storages_child_;
 }
 
@@ -1231,32 +1234,37 @@
     CookieTreeHostNode::GetOrCreateSessionStoragesNode() {
   if (session_storages_child_)
     return session_storages_child_;
-  session_storages_child_ = new CookieTreeSessionStoragesNode;
-  AddChildSortedByTitle(base::WrapUnique(session_storages_child_));
+  auto session_storages_node =
+      std::make_unique<CookieTreeSessionStoragesNode>();
+  session_storages_child_ = session_storages_node.get();
+  AddChildSortedByTitle(std::move(session_storages_node));
   return session_storages_child_;
 }
 
 CookieTreeAppCachesNode* CookieTreeHostNode::GetOrCreateAppCachesNode() {
   if (appcaches_child_)
     return appcaches_child_;
-  appcaches_child_ = new CookieTreeAppCachesNode;
-  AddChildSortedByTitle(base::WrapUnique(appcaches_child_));
+  auto appcaches_node = std::make_unique<CookieTreeAppCachesNode>();
+  appcaches_child_ = appcaches_node.get();
+  AddChildSortedByTitle(std::move(appcaches_node));
   return appcaches_child_;
 }
 
 CookieTreeIndexedDBsNode* CookieTreeHostNode::GetOrCreateIndexedDBsNode() {
   if (indexed_dbs_child_)
     return indexed_dbs_child_;
-  indexed_dbs_child_ = new CookieTreeIndexedDBsNode;
-  AddChildSortedByTitle(base::WrapUnique(indexed_dbs_child_));
+  auto indexed_dbs_node = std::make_unique<CookieTreeIndexedDBsNode>();
+  indexed_dbs_child_ = indexed_dbs_node.get();
+  AddChildSortedByTitle(std::move(indexed_dbs_node));
   return indexed_dbs_child_;
 }
 
 CookieTreeFileSystemsNode* CookieTreeHostNode::GetOrCreateFileSystemsNode() {
   if (file_systems_child_)
     return file_systems_child_;
-  file_systems_child_ = new CookieTreeFileSystemsNode;
-  AddChildSortedByTitle(base::WrapUnique(file_systems_child_));
+  auto file_systems_node = std::make_unique<CookieTreeFileSystemsNode>();
+  file_systems_child_ = file_systems_node.get();
+  AddChildSortedByTitle(std::move(file_systems_node));
   return file_systems_child_;
 }
 
@@ -1264,8 +1272,9 @@
     std::list<BrowsingDataQuotaHelper::QuotaInfo>::iterator quota_info) {
   if (quota_child_)
     return quota_child_;
-  quota_child_ = new CookieTreeQuotaNode(quota_info);
-  AddChildSortedByTitle(base::WrapUnique(quota_child_));
+  auto quota_node = std::make_unique<CookieTreeQuotaNode>(quota_info);
+  quota_child_ = quota_node.get();
+  AddChildSortedByTitle(std::move(quota_node));
   return quota_child_;
 }
 
@@ -1273,8 +1282,9 @@
 CookieTreeHostNode::GetOrCreateServiceWorkersNode() {
   if (service_workers_child_)
     return service_workers_child_;
-  service_workers_child_ = new CookieTreeServiceWorkersNode;
-  AddChildSortedByTitle(base::WrapUnique(service_workers_child_));
+  auto service_workers_node = std::make_unique<CookieTreeServiceWorkersNode>();
+  service_workers_child_ = service_workers_node.get();
+  AddChildSortedByTitle(std::move(service_workers_node));
   return service_workers_child_;
 }
 
@@ -1282,8 +1292,9 @@
 CookieTreeHostNode::GetOrCreateSharedWorkersNode() {
   if (shared_workers_child_)
     return shared_workers_child_;
-  shared_workers_child_ = new CookieTreeSharedWorkersNode;
-  AddChildSortedByTitle(base::WrapUnique(shared_workers_child_));
+  auto shared_workers_node = std::make_unique<CookieTreeSharedWorkersNode>();
+  shared_workers_child_ = shared_workers_node.get();
+  AddChildSortedByTitle(std::move(shared_workers_node));
   return shared_workers_child_;
 }
 
@@ -1291,8 +1302,9 @@
 CookieTreeHostNode::GetOrCreateCacheStoragesNode() {
   if (cache_storages_child_)
     return cache_storages_child_;
-  cache_storages_child_ = new CookieTreeCacheStoragesNode;
-  AddChildSortedByTitle(base::WrapUnique(cache_storages_child_));
+  auto cache_storages_node = std::make_unique<CookieTreeCacheStoragesNode>();
+  cache_storages_child_ = cache_storages_node.get();
+  AddChildSortedByTitle(std::move(cache_storages_node));
   return cache_storages_child_;
 }
 
@@ -1300,8 +1312,9 @@
 CookieTreeHostNode::GetOrCreateMediaLicensesNode() {
   if (media_licenses_child_)
     return media_licenses_child_;
-  media_licenses_child_ = new CookieTreeMediaLicensesNode();
-  AddChildSortedByTitle(base::WrapUnique(media_licenses_child_));
+  auto media_licenses_node = std::make_unique<CookieTreeMediaLicensesNode>();
+  media_licenses_child_ = media_licenses_node.get();
+  AddChildSortedByTitle(std::move(media_licenses_node));
   return media_licenses_child_;
 }
 
@@ -1946,23 +1959,25 @@
   auto* native_io_context = storage_partition->GetNativeIOContext();
 
   auto container = std::make_unique<LocalDataContainer>(
-      new browsing_data::CookieHelper(
+      base::MakeRefCounted<browsing_data::CookieHelper>(
           storage_partition, GetCookieDeletionDisabledCallback(profile)),
-      new browsing_data::DatabaseHelper(profile),
-      new browsing_data::LocalStorageHelper(profile),
+      base::MakeRefCounted<browsing_data::DatabaseHelper>(profile),
+      base::MakeRefCounted<browsing_data::LocalStorageHelper>(profile),
       /*session_storage_helper=*/nullptr,
-      new browsing_data::AppCacheHelper(
+      base::MakeRefCounted<browsing_data::AppCacheHelper>(
           storage_partition->GetAppCacheService()),
-      new browsing_data::IndexedDBHelper(storage_partition),
+      base::MakeRefCounted<browsing_data::IndexedDBHelper>(storage_partition),
       base::MakeRefCounted<browsing_data::FileSystemHelper>(
           file_system_context,
           browsing_data_file_system_util::GetAdditionalFileSystemTypes(),
           native_io_context),
       BrowsingDataQuotaHelper::Create(profile),
-      new browsing_data::ServiceWorkerHelper(
+      base::MakeRefCounted<browsing_data::ServiceWorkerHelper>(
           storage_partition->GetServiceWorkerContext()),
-      new browsing_data::SharedWorkerHelper(storage_partition),
-      new browsing_data::CacheStorageHelper(storage_partition),
+      base::MakeRefCounted<browsing_data::SharedWorkerHelper>(
+          storage_partition),
+      base::MakeRefCounted<browsing_data::CacheStorageHelper>(
+          storage_partition),
       BrowsingDataMediaLicenseHelper::Create(file_system_context));
 
   return std::make_unique<CookiesTreeModel>(
diff --git a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
index 7fe4d62..f75a5f1 100644
--- a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -64,39 +65,48 @@
   void SetUp() override {
     profile_ = std::make_unique<TestingProfile>();
     mock_browsing_data_cookie_helper_ =
-        new browsing_data::MockCookieHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockCookieHelper>(profile_.get());
     mock_browsing_data_database_helper_ =
-        new browsing_data::MockDatabaseHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockDatabaseHelper>(profile_.get());
     mock_browsing_data_local_storage_helper_ =
-        new browsing_data::MockLocalStorageHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockLocalStorageHelper>(
+            profile_.get());
     mock_browsing_data_session_storage_helper_ =
-        new browsing_data::MockLocalStorageHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockLocalStorageHelper>(
+            profile_.get());
     mock_browsing_data_appcache_helper_ =
-        new browsing_data::MockAppCacheHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockAppCacheHelper>(profile_.get());
     mock_browsing_data_indexed_db_helper_ =
-        new browsing_data::MockIndexedDBHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockIndexedDBHelper>(
+            profile_.get());
     mock_browsing_data_file_system_helper_ =
-        new browsing_data::MockFileSystemHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockFileSystemHelper>(
+            profile_.get());
     mock_browsing_data_quota_helper_ =
-        new MockBrowsingDataQuotaHelper(profile_.get());
+        base::MakeRefCounted<MockBrowsingDataQuotaHelper>(profile_.get());
     mock_browsing_data_service_worker_helper_ =
-        new browsing_data::MockServiceWorkerHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockServiceWorkerHelper>(
+            profile_.get());
     mock_browsing_data_shared_worker_helper_ =
-        new browsing_data::MockSharedWorkerHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockSharedWorkerHelper>(
+            profile_.get());
     mock_browsing_data_cache_storage_helper_ =
-        new browsing_data::MockCacheStorageHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockCacheStorageHelper>(
+            profile_.get());
     mock_browsing_data_media_license_helper_ =
-        new MockBrowsingDataMediaLicenseHelper(profile_.get());
+        base::MakeRefCounted<MockBrowsingDataMediaLicenseHelper>(
+            profile_.get());
 
     const char kExtensionScheme[] = "extensionscheme";
-    scoped_refptr<content_settings::CookieSettings> cookie_settings =
-        new content_settings::CookieSettings(
+    auto cookie_settings =
+        base::MakeRefCounted<content_settings::CookieSettings>(
             HostContentSettingsMapFactory::GetForProfile(profile_.get()),
             profile_->GetPrefs(), profile_->IsIncognitoProfile(),
             kExtensionScheme);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     special_storage_policy_ =
-        new ExtensionSpecialStoragePolicy(cookie_settings.get());
+        base::MakeRefCounted<ExtensionSpecialStoragePolicy>(
+            cookie_settings.get());
 #endif
   }
 
diff --git a/chrome/browser/browsing_data/site_data_size_collector_unittest.cc b/chrome/browser/browsing_data/site_data_size_collector_unittest.cc
index f27bc877..b0ef6cd5 100644
--- a/chrome/browser/browsing_data/site_data_size_collector_unittest.cc
+++ b/chrome/browser/browsing_data/site_data_size_collector_unittest.cc
@@ -38,21 +38,26 @@
   void SetUp() override {
     profile_ = std::make_unique<TestingProfile>();
     mock_browsing_data_cookie_helper_ =
-        new browsing_data::MockCookieHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockCookieHelper>(profile_.get());
     mock_browsing_data_database_helper_ =
-        new browsing_data::MockDatabaseHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockDatabaseHelper>(profile_.get());
     mock_browsing_data_local_storage_helper_ =
-        new browsing_data::MockLocalStorageHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockLocalStorageHelper>(
+            profile_.get());
     mock_browsing_data_appcache_helper_ =
-        new browsing_data::MockAppCacheHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockAppCacheHelper>(profile_.get());
     mock_browsing_data_indexed_db_helper_ =
-        new browsing_data::MockIndexedDBHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockIndexedDBHelper>(
+            profile_.get());
     mock_browsing_data_file_system_helper_ =
-        new browsing_data::MockFileSystemHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockFileSystemHelper>(
+            profile_.get());
     mock_browsing_data_service_worker_helper_ =
-        new browsing_data::MockServiceWorkerHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockServiceWorkerHelper>(
+            profile_.get());
     mock_browsing_data_cache_storage_helper_ =
-        new browsing_data::MockCacheStorageHelper(profile_.get());
+        base::MakeRefCounted<browsing_data::MockCacheStorageHelper>(
+            profile_.get());
 
     base::WriteFile(profile_->GetPath().Append(chrome::kCookieFilename),
                     kCookieFileData, base::size(kCookieFileData));
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 0da8ea6..e07aca8 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1593,13 +1593,6 @@
   CloudPrintProxyServiceFactory::GetForProfile(profile_);
 #endif
 
-  // Two different types of hang detection cannot attempt to upload crashes at
-  // the same time or they would interfere with each other.
-  if (!base::HangWatcher::IsCrashReportingEnabled()) {
-    // Start watching all browser threads for responsiveness.
-    ThreadWatcherList::StartWatchingAll(parsed_command_line());
-  }
-
   // This has to come before the first GetInstance() call. PreBrowserStart()
   // seems like a reasonable place to put this, except on Android,
   // OfflinePageInfoHandler::Register() below calls GetInstance().
@@ -1607,10 +1600,6 @@
   sessions::ContentSerializedNavigationDriver::SetInstance(
       ChromeSerializedNavigationDriver::GetInstance());
 
-#if defined(OS_ANDROID)
-  ThreadWatcherAndroid::RegisterApplicationStatusListener();
-#endif  // defined(OS_ANDROID)
-
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
   offline_pages::OfflinePageInfoHandler::Register();
 #endif
@@ -1827,9 +1816,6 @@
   if (notify_result_ == ProcessSingleton::PROCESS_NONE)
     process_singleton_->Cleanup();
 
-  // Stop all tasks that might run on WatchDogThread.
-  ThreadWatcherList::StopWatchingAll();
-
   browser_process_->metrics_service()->Stop();
 
   restart_last_session_ = browser_shutdown::ShutdownPreThreadsStop();
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 56c871a..c09a8d1d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -3158,7 +3158,6 @@
     "secure_channel/secure_channel_client_provider.h",
     "secure_channel/util/histogram_util.cc",
     "secure_channel/util/histogram_util.h",
-    "service_sandbox_type.h",
     "session_length_limiter.cc",
     "session_length_limiter.h",
     "set_time_dialog.cc",
diff --git a/chrome/browser/chromeos/OWNERS b/chrome/browser/chromeos/OWNERS
index 5439524..078bbe0 100644
--- a/chrome/browser/chromeos/OWNERS
+++ b/chrome/browser/chromeos/OWNERS
@@ -11,7 +11,3 @@
 xiyuan@chromium.org
 per-file *active_directory*=file://chrome/browser/ash/authpolicy/OWNERS
 per-file tpm_firmware_update*=mnissler@chromium.org
-
-# Sandbox selection needs security review
-per-file service_sandbox_type.h=set noparent
-per-file service_sandbox_type.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index 38db7c05..43d7b66 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+// TODO(https://crbug.com/1164001): remove and use forward declaration.
+#include "ash/components/power/dark_resume_controller.h"
 #include "base/macros.h"
 #include "base/task/cancelable_task_tracker.h"
 // TODO(https://crbug.com/1164001): remove and use forward declaration.
@@ -14,6 +16,8 @@
 // TODO(https://crbug.com/1164001): remove and use forward declaration.
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
 // TODO(https://crbug.com/1164001): remove and use forward declaration.
+#include "chrome/browser/ash/events/event_rewriter_delegate_impl.h"
+// TODO(https://crbug.com/1164001): remove and use forward declaration.
 #include "chrome/browser/ash/login/demo_mode/demo_mode_resources_remover.h"
 // TODO(https://crbug.com/1164001): remove and use forward declaration.
 #include "chrome/browser/ash/notifications/gnubby_notification.h"
@@ -86,7 +90,6 @@
 
 class BulkPrintersCalculatorFactory;
 class DebugdNotificationHandler;
-class EventRewriterDelegateImpl;
 class FastTransitionObserver;
 class LoginScreenExtensionsLifetimeManager;
 class LoginScreenExtensionsStorageCleaner;
@@ -109,10 +112,6 @@
 class KeyPermissionsManager;
 }
 
-namespace system {
-class DarkResumeController;
-}  // namespace system
-
 // ChromeBrowserMainParts implementation for chromeos specific code.
 // NOTE: Chromeos UI (Ash) support should be added to
 // ChromeBrowserMainExtraPartsAsh instead. This class should not depend on
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc
index b52762a..653dd8d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc
@@ -68,18 +68,53 @@
 SystemNotificationManager::CreateNotification(
     const std::string& notification_id,
     const std::u16string& title,
-    const std::u16string& message) {
+    const std::u16string& message,
+    const base::RepeatingClosure& click_callback) {
   return ash::CreateSystemNotification(
       message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title, message,
       std::u16string(), GURL(), message_center::NotifierId(),
       message_center::RichNotificationData(),
-      new message_center::HandleNotificationClickDelegate(
-          base::BindRepeating(&SystemNotificationManager::Dismiss,
-                              weak_ptr_factory_.GetWeakPtr(), notification_id)),
+      new message_center::HandleNotificationClickDelegate(click_callback),
       kNotificationGoogleIcon,
       message_center::SystemNotificationWarningLevel::NORMAL);
 }
 
+std::unique_ptr<message_center::Notification>
+SystemNotificationManager::CreateNotification(
+    const std::string& notification_id,
+    const std::u16string& title,
+    const std::u16string& message) {
+  return CreateNotification(
+      notification_id, title, message,
+      base::BindRepeating(&SystemNotificationManager::Dismiss,
+                          weak_ptr_factory_.GetWeakPtr(), notification_id));
+}
+
+std::unique_ptr<message_center::Notification>
+SystemNotificationManager::CreateNotification(
+    const std::string& notification_id,
+    int title_id,
+    int message_id) {
+  std::u16string title = l10n_util::GetStringUTF16(title_id);
+  std::u16string message = l10n_util::GetStringUTF16(message_id);
+  return CreateNotification(notification_id, title, message);
+}
+
+std::unique_ptr<message_center::Notification>
+SystemNotificationManager::CreateNotification(
+    const std::string& notification_id,
+    int title_id,
+    int message_id,
+    const scoped_refptr<message_center::NotificationDelegate>& delegate) {
+  std::u16string title = l10n_util::GetStringUTF16(title_id);
+  std::u16string message = l10n_util::GetStringUTF16(message_id);
+  return ash::CreateSystemNotification(
+      message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title, message,
+      std::u16string(), GURL(), message_center::NotifierId(),
+      message_center::RichNotificationData(), delegate, kNotificationGoogleIcon,
+      message_center::SystemNotificationWarningLevel::NORMAL);
+}
+
 void SystemNotificationManager::HandleProgressClick(
     const std::string& notification_id,
     absl::optional<int> button_index) {
@@ -117,23 +152,6 @@
       message_center::SystemNotificationWarningLevel::NORMAL);
 }
 
-std::unique_ptr<message_center::Notification>
-SystemNotificationManager::CreateNotification(
-    const std::string& notification_id,
-    int title_id,
-    int message_id) {
-  return ash::CreateSystemNotification(
-      message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
-      l10n_util::GetStringUTF16(title_id),
-      l10n_util::GetStringUTF16(message_id), std::u16string(), GURL(),
-      message_center::NotifierId(), message_center::RichNotificationData(),
-      new message_center::HandleNotificationClickDelegate(
-          base::BindRepeating(&SystemNotificationManager::Dismiss,
-                              weak_ptr_factory_.GetWeakPtr(), notification_id)),
-      kNotificationGoogleIcon,
-      message_center::SystemNotificationWarningLevel::NORMAL);
-}
-
 void SystemNotificationManager::Dismiss(const std::string& notification_id) {
   GetNotificationDisplayService()->Close(NotificationHandler::Type::TRANSIENT,
                                          notification_id);
@@ -295,17 +313,13 @@
           event_arguments[0], &dialog_event)) {
     std::vector<message_center::ButtonInfo> notification_buttons;
     id = file_manager_private::ToString(dialog_event.type);
-    notification = ash::CreateSystemNotification(
-        message_center::NOTIFICATION_TYPE_SIMPLE, kDriveDialogId,
-        l10n_util::GetStringUTF16(IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL),
-        l10n_util::GetStringUTF16(IDS_FILE_BROWSER_OFFLINE_ENABLE_MESSAGE),
-        std::u16string(), GURL(), message_center::NotifierId(),
-        message_center::RichNotificationData(),
+    scoped_refptr<message_center::NotificationDelegate> delegate =
         new message_center::HandleNotificationClickDelegate(base::BindRepeating(
             &SystemNotificationManager::HandleDriveDialogClick,
-            weak_ptr_factory_.GetWeakPtr())),
-        kNotificationGoogleIcon,
-        message_center::SystemNotificationWarningLevel::NORMAL);
+            weak_ptr_factory_.GetWeakPtr()));
+    notification = CreateNotification(
+        kDriveDialogId, IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL,
+        IDS_FILE_BROWSER_OFFLINE_ENABLE_MESSAGE, delegate);
 
     notification_buttons.push_back(message_center::ButtonInfo(
         l10n_util::GetStringUTF16(IDS_FILE_BROWSER_OFFLINE_ENABLE_REJECT)));
@@ -448,27 +462,49 @@
 }
 
 std::unique_ptr<message_center::Notification>
+SystemNotificationManager::MakeMountErrorNotification(
+    file_manager_private::MountCompletedEvent& event,
+    const Volume& volume) {
+  std::unique_ptr<message_center::Notification> notification;
+  scoped_refptr<message_center::NotificationDelegate> delegate =
+      new message_center::HandleNotificationClickDelegate(base::BindRepeating(
+          &SystemNotificationManager::HandleRemovableNotificationClick,
+          weak_ptr_factory_.GetWeakPtr(), volume.mount_path().value()));
+  switch (event.status) {
+    case file_manager_private::
+        MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM:
+      notification = CreateNotification(
+          kRemovableNotificationId, IDS_REMOVABLE_DEVICE_DETECTION_TITLE,
+          IDS_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE, delegate);
+      break;
+    default:
+      DLOG(WARNING) << "Unhandled mount error for " << event.status;
+      break;
+  }
+  return notification;
+}
+
+std::unique_ptr<message_center::Notification>
 SystemNotificationManager::MakeRemovableNotification(
     file_manager_private::MountCompletedEvent& event,
     const Volume& volume) {
+  if (event.status != file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS) {
+    return MakeMountErrorNotification(event, volume);
+  }
   int message_id;
   if (volume.is_read_only() && !volume.is_read_only_removable_device()) {
     message_id = IDS_REMOVABLE_DEVICE_NAVIGATION_MESSAGE_READONLY_POLICY;
   } else {
     message_id = IDS_REMOVABLE_DEVICE_NAVIGATION_MESSAGE;
   }
+  scoped_refptr<message_center::NotificationDelegate> delegate =
+      new message_center::HandleNotificationClickDelegate(base::BindRepeating(
+          &SystemNotificationManager::HandleRemovableNotificationClick,
+          weak_ptr_factory_.GetWeakPtr(), volume.mount_path().value()));
   std::unique_ptr<message_center::Notification> notification =
-      ash::CreateSystemNotification(
-          message_center::NOTIFICATION_TYPE_SIMPLE, kRemovableNotificationId,
-          l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_DETECTION_TITLE),
-          l10n_util::GetStringUTF16(message_id), std::u16string(), GURL(),
-          message_center::NotifierId(), message_center::RichNotificationData(),
-          new message_center::HandleNotificationClickDelegate(
-              base::BindRepeating(
-                  &SystemNotificationManager::HandleRemovableNotificationClick,
-                  weak_ptr_factory_.GetWeakPtr(), volume.mount_path().value())),
-          kNotificationGoogleIcon,
-          message_center::SystemNotificationWarningLevel::NORMAL);
+      CreateNotification(kRemovableNotificationId,
+                         IDS_REMOVABLE_DEVICE_DETECTION_TITLE, message_id,
+                         delegate);
 
   std::vector<message_center::ButtonInfo> notification_buttons;
   notification_buttons.push_back(message_center::ButtonInfo(
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h
index e1cd039..22c108ee 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.h
@@ -43,6 +43,25 @@
   void HandleDeviceEvent(const file_manager_private::DeviceEvent& event);
 
   /**
+   *  Returns an instance of an 'ash' Notification with a bound click callback.
+   */
+  std::unique_ptr<message_center::Notification> CreateNotification(
+      const std::string& notification_id,
+      const std::u16string& title,
+      const std::u16string& message,
+      const base::RepeatingClosure& click_callback);
+
+  /**
+   *  Returns an instance of an 'ash' Notification with title and message
+   *  specified by string ID values (for 110n) with a bound click delegate.
+   */
+  std::unique_ptr<message_center::Notification> CreateNotification(
+      const std::string& notification_id,
+      int title_id,
+      int message_id,
+      const scoped_refptr<message_center::NotificationDelegate>& delegate);
+
+  /**
    *  Returns an instance of an 'ash' Notification.
    */
   std::unique_ptr<message_center::Notification> CreateNotification(
@@ -135,6 +154,13 @@
                            absl::optional<int> button_index);
 
   /**
+   * Makes a notification instance for mount errors.
+   */
+  std::unique_ptr<message_center::Notification> MakeMountErrorNotification(
+      file_manager_private::MountCompletedEvent& event,
+      const Volume& volume);
+
+  /**
    * Makes a notification instance for removable devices.
    */
   std::unique_ptr<message_center::Notification> MakeRemovableNotification(
diff --git a/chrome/browser/chromeos/service_sandbox_type.h b/chrome/browser/chromeos/service_sandbox_type.h
deleted file mode 100644
index c92d707..0000000
--- a/chrome/browser/chromeos/service_sandbox_type.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_SERVICE_SANDBOX_TYPE_H_
-#define CHROME_BROWSER_CHROMEOS_SERVICE_SANDBOX_TYPE_H_
-
-#include "build/chromeos_buildflags.h"
-#include "chromeos/assistant/buildflags.h"
-#include "content/public/browser/service_process_host.h"
-#include "sandbox/policy/sandbox_type.h"
-
-// This file maps service classes to sandbox types. See
-// ServiceProcessHost::Launch() for how these templates are consumed.
-
-// chromeos::ime::mojom::ImeService
-namespace chromeos {
-namespace ime {
-namespace mojom {
-class ImeService;
-}  // namespace mojom
-}  // namespace ime
-}  // namespace chromeos
-
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<chromeos::ime::mojom::ImeService>() {
-  return sandbox::policy::SandboxType::kIme;
-}
-
-// chromeos::tts::mojom::TtsService
-namespace chromeos {
-namespace tts {
-namespace mojom {
-class TtsService;
-}  // namespace mojom
-}  // namespace tts
-}  // namespace chromeos
-
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<chromeos::tts::mojom::TtsService>() {
-  return sandbox::policy::SandboxType::kTts;
-}
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// recording::mojom::RecordingService
-namespace recording {
-namespace mojom {
-class RecordingService;
-}  // namespace mojom
-}  // namespace recording
-
-// This is needed to prevent the service from crashing on a sandbox seccomp-bpf
-// failure when the audio capturer tries to open a stream.
-// TODO(https://crbug.com/1147991): Explore alternatives if any.
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<recording::mojom::RecordingService>() {
-  return sandbox::policy::SandboxType::kVideoCapture;
-}
-
-// chromeos::assistant::mojom::AssistantAudioDecoderFactory
-namespace chromeos {
-namespace assistant {
-namespace mojom {
-class AssistantAudioDecoderFactory;
-}  // namespace mojom
-}  // namespace assistant
-}  // namespace chromeos
-
-template <>
-inline sandbox::policy::SandboxType content::GetServiceSandboxType<
-    chromeos::assistant::mojom::AssistantAudioDecoderFactory>() {
-  return sandbox::policy::SandboxType::kUtility;
-}
-
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-// chromeos::libassistant::mojom::LibassistantService
-namespace chromeos {
-namespace libassistant {
-namespace mojom {
-class LibassistantService;
-}  // namespace mojom
-}  // namespace libassistant
-}  // namespace chromeos
-
-template <>
-inline sandbox::policy::SandboxType content::GetServiceSandboxType<
-    chromeos::libassistant::mojom::LibassistantService>() {
-  return sandbox::policy::SandboxType::kLibassistant;
-}
-#endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#endif  // CHROME_BROWSER_CHROMEOS_SERVICE_SANDBOX_TYPE_H_
diff --git a/chrome/browser/download/internal/android/java/res/layout/download_manager_section_header.xml b/chrome/browser/download/internal/android/java/res/layout/download_manager_section_header.xml
index 348feba..7dd2bbe 100644
--- a/chrome/browser/download/internal/android/java/res/layout/download_manager_section_header.xml
+++ b/chrome/browser/download/internal/android/java/res/layout/download_manager_section_header.xml
@@ -8,16 +8,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
-    <Space
-        android:id="@+id/top_space"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/download_manager_section_title_padding_top" />
-
     <TextView
         android:id="@+id/date"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_below="@+id/top_space"
+        android:layout_marginTop="@dimen/download_manager_section_title_top_margin"
+        android:layout_marginBottom="@dimen/download_manager_section_title_bottom_margin"
         android:paddingStart="@dimen/list_item_default_margin"
         android:maxLines="1"
         android:textAppearance="@style/TextAppearance.TextLarge.Primary"/>
diff --git a/chrome/browser/download/internal/android/java/res/values-v17/dimens.xml b/chrome/browser/download/internal/android/java/res/values-v17/dimens.xml
index 144d25e..bb4fcce 100644
--- a/chrome/browser/download/internal/android/java/res/values-v17/dimens.xml
+++ b/chrome/browser/download/internal/android/java/res/values-v17/dimens.xml
@@ -11,7 +11,9 @@
     <dimen name="download_manager_max_image_item_width_wide_screen">300dp</dimen>
     <dimen name="download_manager_prefetch_vertical_margin">12dp</dimen>
     <dimen name="download_manager_recycler_view_min_padding_wide_screen">16dp</dimen>
-    <dimen name="download_manager_section_title_padding_top">16dp</dimen>
+    <dimen name="download_manager_section_title_top_margin">16dp</dimen>
+    <dimen name="download_manager_section_title_bottom_margin">8dp</dimen>
+    <dimen name="download_manager_vertical_margin_between_download_types">8dp</dimen>
     <!-- The corner radius is calculated by subtracting the hairline border width from the
          background card corner radius. -->
     <dimen name="download_manager_thumbnail_corner_radius">7dp</dimen>
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index d6ee183d..112bc69 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -21,6 +21,7 @@
 
 import org.chromium.chrome.browser.download.home.DownloadManagerUiConfig;
 import org.chromium.chrome.browser.download.home.list.DateOrderedListCoordinator.DateOrderedListObserver;
+import org.chromium.chrome.browser.download.home.list.ListItem.OfflineItemListItem;
 import org.chromium.chrome.browser.download.home.list.holder.ListItemViewHolder;
 import org.chromium.chrome.browser.download.internal.R;
 import org.chromium.components.browser_ui.widget.displaystyle.HorizontalDisplayStyle;
@@ -41,6 +42,7 @@
     private final int mInterImagePaddingPx;
     private final int mPrefetchVerticalPaddingPx;
     private final int mHorizontalPaddingPx;
+    private final int mVerticalPaddingPx;
     private final int mMaxWidthImageItemPx;
 
     private final RecyclerView mView;
@@ -61,6 +63,8 @@
                 R.dimen.download_manager_horizontal_margin);
         mPrefetchVerticalPaddingPx = context.getResources().getDimensionPixelSize(
                 R.dimen.download_manager_prefetch_vertical_margin);
+        mVerticalPaddingPx = context.getResources().getDimensionPixelSize(
+                R.dimen.download_manager_vertical_margin_between_download_types);
         mMaxWidthImageItemPx = context.getResources().getDimensionPixelSize(
                 R.dimen.download_manager_max_image_item_width_wide_screen);
 
@@ -242,6 +246,11 @@
                             == HorizontalDisplayStyle.WIDE) {
                 outRect.right += Math.max(getAvailableViewWidth() - mMaxWidthImageItemPx, 0);
             }
+
+            if (item instanceof ListItem.OfflineItemListItem
+                    && ((OfflineItemListItem) item).isLastOfDownloadTypeInSection) {
+                outRect.bottom += mVerticalPaddingPx;
+            }
         }
 
         private void computeItemDecoration(int position, Rect outRect) {
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListItem.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListItem.java
index 9fcbf06..93a33de 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListItem.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/ListItem.java
@@ -174,6 +174,7 @@
         public OfflineItem item;
         public boolean spanFullWidth;
         public boolean isGrouped;
+        public boolean isLastOfDownloadTypeInSection;
 
         /** Creates an {@link OfflineItemListItem} wrapping {@code item}. */
         public OfflineItemListItem(OfflineItem item) {
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/mutator/ListItemPropertySetter.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/mutator/ListItemPropertySetter.java
index 874ad42..b96766f5 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/mutator/ListItemPropertySetter.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/list/mutator/ListItemPropertySetter.java
@@ -15,6 +15,7 @@
  * Post processes the items in the list and sets properties for UI as appropriate. The properties
  * being set are:
  * - Image item span width.
+ * - Margins between {@link OfflineItemFilter} types (image, document, etc.).
  */
 public class ListItemPropertySetter implements ListConsumer {
     private final DownloadManagerUiConfig mConfig;
@@ -40,6 +41,7 @@
     /** Sets properties for items in the given list. */
     private void setProperties(List<ListItem> sortedList) {
         setWidthForImageItems(sortedList);
+        setMarginsBetweenDifferentItemTypes(sortedList);
     }
 
     private void setWidthForImageItems(List<ListItem> listItems) {
@@ -63,4 +65,25 @@
             }
         }
     }
+
+    private void setMarginsBetweenDifferentItemTypes(List<ListItem> listItems) {
+        for (int i = 0; i < listItems.size(); i++) {
+            ListItem currentItem = listItems.get(i);
+
+            // Margins should only be added to OfflineItems
+            if (!(currentItem instanceof OfflineItemListItem)) {
+                continue;
+            }
+
+            ListItem nextItem = i >= listItems.size() - 1 ? null : listItems.get(i + 1);
+            boolean nextItemIsDifferentType = nextItem instanceof OfflineItemListItem
+                    && ((OfflineItemListItem) currentItem).item.filter
+                            != ((OfflineItemListItem) nextItem).item.filter;
+            boolean nextItemIsNotOfflineItem = !(nextItem instanceof OfflineItemListItem);
+
+            if (nextItemIsDifferentType || nextItemIsNotOfflineItem) {
+                ((OfflineItemListItem) currentItem).isLastOfDownloadTypeInSection = true;
+            }
+        }
+    }
 }
diff --git a/chrome/browser/enterprise/connectors/file_system/box_captured_sites_interactive_uitest.cc b/chrome/browser/enterprise/connectors/file_system/box_captured_sites_interactive_uitest.cc
index 378a1e2..66f8bf23 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_captured_sites_interactive_uitest.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_captured_sites_interactive_uitest.cc
@@ -450,7 +450,6 @@
 
   void CancelBoxSignInConfirmation() {
     signin_confirmation_dlg_->Cancel();
-    WaitForSignInDialogToShow();
   }
 
   // Bypass Single-Factor-Authentication sign in and authorize
@@ -1029,6 +1028,42 @@
             download_item_observer.upload_observer()->GetFileUrl());
 }
 
+IN_PROC_BROWSER_TEST_F(BoxCapturedSitesInteractiveTest, EnterpriseIdMismatch) {
+  SetCloudFSCPolicy(GetAllAllowedTestPolicy("123456789"));
+  StartWprUsingFSCCaptureDir("box.com.ent.id.mismatch.wpr");
+
+  StartDownloadByNavigatingToEmbeddedServerUrl(
+      "/enterprise/connectors/file_system/downloads/cipd/"
+      "direct_download_gibben.epub");
+  BoxDownloadItemObserver download_item_observer(
+      download_manager_observer()->GetLatestDownloadItem());
+
+  download_item_observer.WaitForSignInConfirmationDialog();
+  download_item_observer.sign_in_observer()->AcceptBoxSigninConfirmation();
+
+  // Bypass the Box signin and authorize dialog.
+  download_item_observer.sign_in_observer()->AuthorizeWithUserAndPasswordSFA(
+      GetBoxAccountUserName(), GetBoxAccountPassword());
+  EXPECT_FALSE(
+      download_item_observer.fetch_access_token_observer()->WaitForFetch());
+
+  // The sign in confirmation dialog will relaunch after the enterprise ID
+  // mismatch error. Close the confirmation dialog.
+  download_item_observer.WaitForSignInConfirmationDialog();
+  download_item_observer.sign_in_observer()->CancelBoxSignInConfirmation();
+
+  download_manager_observer()->WaitForDownloadToFinish();
+  EXPECT_TRUE(
+      download_item_observer.upload_observer()->WaitForTmpFileDeletion());
+
+  // Check that the download shelf is displaying the expected "upload
+  // cancelled" text.
+  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
+  DownloadItemView* item_view = GetItemViewForLastDownload();
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED),
+            item_view->GetStatusTextForTesting());
+}
+
 IN_PROC_BROWSER_TEST_F(BoxCapturedSitesInteractiveTest,
                        CancelSignInConfirmation) {
   SetCloudFSCPolicy(GetAllAllowedTestPolicy("797972721"));
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index 07e39eb..9a546676 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -427,13 +427,7 @@
       << message_;
 }
 
-// TODO(http://crbug.com/1213987): flaky on ChromeOS.
-#if defined(OS_CHROMEOS)
-#define MAYBE_DesktopInitialFocus DISABLED_DesktopInitialFocus
-#else
-#define MAYBE_DesktopInitialFocus DesktopInitialFocus
-#endif
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_DesktopInitialFocus) {
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopInitialFocus) {
   ASSERT_TRUE(RunExtensionTest("automation/tests/desktop",
                                {.page_url = "initial_focus.html"}))
       << message_;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index ba692bca..f8df6a9 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2041,11 +2041,6 @@
     "expiry_milestone": 100
   },
   {
-    "name": "enable-ios-managed-settings-ui",
-    "owners": [ "tinazwang", "rohitrao", "bling-flags@google.com" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "enable-javascript-harmony",
     "owners": [ "adamk", "hablich" ],
     // This flag is used by web developers to test upcoming javascript features.
@@ -4370,7 +4365,7 @@
     // TODO(b/172341945): Fix this asap.
     "name": "overscroll-history-navigation",
     "owners": [ "mohsen", "jinsukkim" ],
-    "expiry_milestone": 93
+    "expiry_milestone": 105
   },
   {
     "name": "page-info-discoverability",
@@ -5215,8 +5210,8 @@
   },
   {
     "name": "split-settings-sync",
-    "owners": [ "jamescook", "cros-system-services@google.com" ],
-    "expiry_milestone": 92
+    "owners": [ "rsorokin", "cros-oac@google.com" ],
+    "expiry_milestone": 98
   },
   {
     "name": "start-surface",
diff --git a/chrome/browser/image_editor/screenshot_flow.cc b/chrome/browser/image_editor/screenshot_flow.cc
index fce9bf8..810c6a2 100644
--- a/chrome/browser/image_editor/screenshot_flow.cc
+++ b/chrome/browser/image_editor/screenshot_flow.cc
@@ -22,6 +22,11 @@
 #include "ui/snapshot/snapshot.h"
 #include "ui/views/background.h"
 
+#if defined(OS_MAC)
+#include "content/public/browser/render_view_host.h"
+#include "ui/views/widget/widget.h"
+#endif
+
 #if defined(USE_AURA)
 #include "ui/aura/window.h"
 #include "ui/wm/core/window_util.h"
@@ -62,29 +67,39 @@
   if (screen_capture_layer_)
     return;
 
-#if defined(OS_MAC)
-  return;
-#else
-  const gfx::NativeWindow& native_window = web_contents_->GetNativeView();
-
   screen_capture_layer_ =
       std::make_unique<ui::Layer>(ui::LayerType::LAYER_TEXTURED);
   screen_capture_layer_->SetName("ScreenshotRegionSelectionLayer");
   screen_capture_layer_->SetFillsBoundsOpaquely(true);
   screen_capture_layer_->set_delegate(this);
+#if defined(OS_MAC)
+  gfx::Rect bounds = web_contents_->GetViewBounds();
+  const gfx::NativeView web_contents_view =
+      web_contents_->GetContentNativeView();
+  views::Widget* widget =
+      views::Widget::GetWidgetForNativeView(web_contents_view);
+  ui::Layer* content_layer = widget->GetLayer();
+  const gfx::Rect offset_bounds = widget->GetWindowBoundsInScreen();
+  bounds.Offset(-offset_bounds.x(), -offset_bounds.y());
 
-  ui::Layer* native_window_layer = native_window->layer();
-  native_window_layer->Add(screen_capture_layer_.get());
-  native_window_layer->StackAtTop(screen_capture_layer_.get());
-
-  screen_capture_layer_->SetBounds(native_window->bounds());
-  screen_capture_layer_->SetVisible(true);
-
+  views::Widget* top_widget =
+      views::Widget::GetTopLevelWidgetForNativeView(web_contents_view);
+  views::View* root_view = top_widget->GetRootView();
+  root_view->AddPreTargetHandler(this);
+#else
+  const gfx::NativeWindow& native_window = web_contents_->GetNativeView();
+  ui::Layer* content_layer = native_window->layer();
+  const gfx::Rect bounds = native_window->bounds();
   // Capture mouse down and drag events on our window.
   // TODO(skare): We should exit from this mode when moving between tabs,
   // clicking on browser chrome, etc.
   native_window->AddPreTargetHandler(this);
 #endif
+
+  content_layer->Add(screen_capture_layer_.get());
+  content_layer->StackAtTop(screen_capture_layer_.get());
+  screen_capture_layer_->SetBounds(bounds);
+  screen_capture_layer_->SetVisible(true);
 }
 
 void ScreenshotFlow::RemoveUIOverlay() {
@@ -92,31 +107,42 @@
     return;
 
 #if defined(OS_MAC)
-  return;
+  views::Widget* widget = views::Widget::GetWidgetForNativeView(
+      web_contents_->GetContentNativeView());
+  ui::Layer* content_layer = widget->GetLayer();
+  views::View* root_view = widget->GetRootView();
+  root_view->RemovePreTargetHandler(this);
 #else
   // TODO(skare): Fix case of web_contents_ going away.
   // Otherwise we can crash on shutdown while the capture mode is active.
   const gfx::NativeWindow& native_window = web_contents_->GetNativeView();
   native_window->RemovePreTargetHandler(this);
+  ui::Layer* content_layer = native_window->layer();
+#endif
 
-  ui::Layer* native_window_layer = native_window->layer();
-  native_window_layer->Remove(screen_capture_layer_.get());
+  content_layer->Remove(screen_capture_layer_.get());
+
   screen_capture_layer_->set_delegate(nullptr);
   screen_capture_layer_.reset();
-#endif
 }
 
 void ScreenshotFlow::Start(ScreenshotCaptureCallback flow_callback) {
-#if defined(OS_MAC)
-  return;
-#else
   flow_callback_ = std::move(flow_callback);
+#if defined(OS_MAC)
+  const gfx::NativeView& native_view = web_contents_->GetContentNativeView();
+  gfx::Image img;
+  bool rval = ui::GrabViewSnapshot(native_view,
+                                   gfx::Rect(web_contents_->GetSize()), &img);
+  // If |img| is empty, clients should treat it as a canceled action, but
+  // we have a DCHECK for development as we expected this call to succeed.
+  DCHECK(rval);
+  OnSnapshotComplete(img);
+#else
   // Start the capture process by capturing the entire window, then allow
   // the user to drag out a selection mask.
   ui::GrabWindowSnapshotAsyncCallback screenshot_callback =
       base::BindOnce(&ScreenshotFlow::OnSnapshotComplete, weak_this_);
   const gfx::NativeWindow& native_window = web_contents_->GetNativeView();
-  native_window->GetRootWindow();
   // TODO(skare): Evaluate against other screenshot capture methods.
   // The synchronous variant mentions support is different between platforms
   // and another library might be better if there is a browser process.
@@ -180,6 +206,7 @@
     canvas.DrawImageInt(image_foreground_, region.x(), region.y(), w, h, 0, 0,
                         w, h, false);
     result.image = gfx::Image::CreateFrom1xBitmap(canvas.GetBitmap());
+    result.screen_bounds = screen_capture_layer_->bounds();
   }
 
   RemoveUIOverlay();
diff --git a/chrome/browser/image_editor/screenshot_flow.h b/chrome/browser/image_editor/screenshot_flow.h
index 719ea7d..2a9ef46 100644
--- a/chrome/browser/image_editor/screenshot_flow.h
+++ b/chrome/browser/image_editor/screenshot_flow.h
@@ -34,6 +34,8 @@
 struct ScreenshotCaptureResult {
   // The image obtained from capture. Empty on failure.
   gfx::Image image;
+  // The bounds of the screen during which capture took place. Empty on failure.
+  gfx::Rect screen_bounds;
 };
 
 // Callback for obtaining image data.
diff --git a/chrome/browser/lacros/download_controller_client_lacros.cc b/chrome/browser/lacros/download_controller_client_lacros.cc
index b7ea0b36..02c050387 100644
--- a/chrome/browser/lacros/download_controller_client_lacros.cc
+++ b/chrome/browser/lacros/download_controller_client_lacros.cc
@@ -6,12 +6,13 @@
 
 #include "base/bind.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/crosapi/mojom/download_controller.mojom.h"
 #include "chromeos/lacros/lacros_service.h"
 #include "components/download/public/common/download_item.h"
+#include "components/download/public/common/simple_download_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
-#include "content/public/browser/download_manager.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace {
@@ -58,124 +59,6 @@
 
 }  // namespace
 
-// A wrapper for `base::ScopedObservation` and `DownloadManageObserver` that
-// allows us to keep the manager associated with its `OnManagerInitialized()`
-// event. This prevents us from having to check every manager and profile when
-// a single manager is updated, since `OnManagerInitialized()` does not pass a
-// pointer to the relevant `content::DownloadManager`.
-class DownloadControllerClientLacros::ObservableDownloadManager
-    : public content::DownloadManager::Observer,
-      public download::DownloadItem::Observer {
- public:
-  ObservableDownloadManager(DownloadControllerClientLacros* controller_client,
-                            content::DownloadManager* manager)
-      : controller_client_(controller_client), manager_(manager) {
-    download_manager_observer_.Observe(manager);
-    if (manager->IsManagerInitialized())
-      OnManagerInitialized();
-  }
-
-  ~ObservableDownloadManager() override = default;
-
-  // Returns all downloads, no matter the type or state.
-  std::vector<download::DownloadItem*> GetAllDownloads() {
-    download::SimpleDownloadManager::DownloadVector downloads;
-    if (manager_->IsManagerInitialized())
-      manager_->GetAllDownloads(&downloads);
-    return downloads;
-  }
-
-  // Pauses the download associated with the specified `download_guid`.
-  void Pause(const std::string& download_guid) {
-    auto* download = manager_->GetDownloadByGuid(download_guid);
-    if (download)
-      download->Pause();
-  }
-
-  // Resumes the download associated with the specified `download_guid`. If
-  // `user_resume` is `true`, it signifies that this invocation was triggered by
-  // an explicit user action.
-  void Resume(const std::string& download_guid, bool user_resume) {
-    auto* download = manager_->GetDownloadByGuid(download_guid);
-    if (download)
-      download->Resume(user_resume);
-  }
-
-  // Cancels the download associated with the specified `download_guid`. If
-  // `user_cancel` is `true`, it signifies that this invocation was triggered by
-  // an explicit user action.
-  void Cancel(const std::string& download_guid, bool user_cancel) {
-    auto* download = manager_->GetDownloadByGuid(download_guid);
-    if (download)
-      download->Cancel(user_cancel);
-  }
-
-  // Marks the download associated with the specified `download_guid` to be
-  // `open_when_complete`.
-  void SetOpenWhenComplete(const std::string& download_guid,
-                           bool open_when_complete) {
-    auto* download = manager_->GetDownloadByGuid(download_guid);
-    if (download)
-      download->SetOpenWhenComplete(open_when_complete);
-  }
-
- private:
-  // content::DownloadManager::Observer:
-  void OnManagerInitialized() override {
-    download::SimpleDownloadManager::DownloadVector downloads;
-    manager_->GetAllDownloads(&downloads);
-
-    for (auto* download : downloads) {
-      download_item_observer_.AddObservation(download);
-      controller_client_->OnDownloadCreated(download);
-    }
-  }
-
-  void ManagerGoingDown(content::DownloadManager* manager) override {
-    download_manager_observer_.Reset();
-    // Manually call the destroyed event for each download, because this
-    // `ObservableDownloadManager` will be destroyed before we receive them.
-    download::SimpleDownloadManager::DownloadVector downloads;
-    manager->GetAllDownloads(&downloads);
-
-    for (auto* download : downloads)
-      OnDownloadDestroyed(download);
-
-    controller_client_->OnManagerGoingDown(this);
-  }
-
-  void OnDownloadCreated(content::DownloadManager* manager,
-                         download::DownloadItem* item) override {
-    if (!manager->IsManagerInitialized())
-      return;
-    download_item_observer_.AddObservation(item);
-    controller_client_->OnDownloadCreated(item);
-  }
-
-  // download::DownloadItem::Observer:
-  void OnDownloadUpdated(download::DownloadItem* item) override {
-    controller_client_->OnDownloadUpdated(item);
-  }
-
-  void OnDownloadDestroyed(download::DownloadItem* item) override {
-    if (download_item_observer_.IsObservingSource(item))
-      download_item_observer_.RemoveObservation(item);
-    controller_client_->OnDownloadDestroyed(item);
-  }
-
-  DownloadControllerClientLacros* const controller_client_;
-
-  content::DownloadManager* const manager_;
-
-  base::ScopedMultiSourceObservation<download::DownloadItem,
-                                     download::DownloadItem::Observer>
-      download_item_observer_{this};
-
-  base::ScopedObservation<content::DownloadManager,
-                          content::DownloadManager::Observer>
-      download_manager_observer_{this};
-};
-
 DownloadControllerClientLacros::DownloadControllerClientLacros() {
   g_browser_process->profile_manager()->AddObserver(this);
   auto profiles = g_browser_process->profile_manager()->GetLoadedProfiles();
@@ -209,10 +92,8 @@
   std::vector<crosapi::mojom::DownloadItemPtr> downloads;
 
   // Aggregate all downloads.
-  for (auto& observable_download_manager : observable_download_managers_) {
-    for (auto* download : observable_download_manager->GetAllDownloads())
-      downloads.push_back(ConvertToMojoDownloadItem(download));
-  }
+  for (auto* download : download_notifier_.GetAllDownloads())
+    downloads.push_back(ConvertToMojoDownloadItem(download));
 
   // Sort chronologically by start time.
   std::sort(downloads.begin(), downloads.end(),
@@ -225,56 +106,55 @@
 }
 
 void DownloadControllerClientLacros::Pause(const std::string& download_guid) {
-  for (auto& observable_download_manager : observable_download_managers_)
-    observable_download_manager->Pause(download_guid);
+  auto* download = download_notifier_.GetDownloadByGuid(download_guid);
+  if (download)
+    download->Pause();
 }
 
 void DownloadControllerClientLacros::Resume(const std::string& download_guid,
                                             bool user_resume) {
-  for (auto& observable_download_manager : observable_download_managers_)
-    observable_download_manager->Resume(download_guid, user_resume);
+  auto* download = download_notifier_.GetDownloadByGuid(download_guid);
+  if (download)
+    download->Resume(user_resume);
 }
 
 void DownloadControllerClientLacros::Cancel(const std::string& download_guid,
                                             bool user_cancel) {
-  for (auto& observable_download_manager : observable_download_managers_)
-    observable_download_manager->Cancel(download_guid, user_cancel);
+  auto* download = download_notifier_.GetDownloadByGuid(download_guid);
+  if (download)
+    download->Cancel(user_cancel);
 }
 
 void DownloadControllerClientLacros::SetOpenWhenComplete(
     const std::string& download_guid,
     bool open_when_complete) {
-  for (auto& observable_download_manager : observable_download_managers_) {
-    observable_download_manager->SetOpenWhenComplete(download_guid,
-                                                     open_when_complete);
-  }
+  auto* download = download_notifier_.GetDownloadByGuid(download_guid);
+  if (download)
+    download->SetOpenWhenComplete(open_when_complete);
 }
 
 void DownloadControllerClientLacros::OnProfileAdded(Profile* profile) {
-  profile_observer_.AddObservation(profile);
-  auto* manager = profile->GetDownloadManager();
-  observable_download_managers_.emplace(
-      std::make_unique<ObservableDownloadManager>(this, manager));
+  download_notifier_.AddProfile(profile);
 }
 
-void DownloadControllerClientLacros::OnOffTheRecordProfileCreated(
-    Profile* off_the_record) {
-  OnProfileAdded(off_the_record);
-}
-
-void DownloadControllerClientLacros::OnProfileWillBeDestroyed(
-    Profile* profile) {
-  profile_observer_.RemoveObservation(profile);
+void DownloadControllerClientLacros::OnManagerInitialized(
+    content::DownloadManager* manager) {
+  download::SimpleDownloadManager::DownloadVector downloads;
+  manager->GetAllDownloads(&downloads);
+  for (auto* download : downloads)
+    OnDownloadCreated(manager, download);
 }
 
 void DownloadControllerClientLacros::OnManagerGoingDown(
-    ObservableDownloadManager* observable_manager) {
-  auto it = observable_download_managers_.find(observable_manager);
-  DCHECK_NE(it->get(), observable_download_managers_.end()->get());
-  observable_download_managers_.erase(it);
+    content::DownloadManager* manager) {
+  download::SimpleDownloadManager::DownloadVector downloads;
+  manager->GetAllDownloads(&downloads);
+  for (auto* download : downloads)
+    OnDownloadDestroyed(manager, download);
 }
 
 void DownloadControllerClientLacros::OnDownloadCreated(
+    content::DownloadManager* manager,
     download::DownloadItem* item) {
   auto* service = chromeos::LacrosService::Get();
   if (!service->IsAvailable<crosapi::mojom::DownloadController>())
@@ -285,6 +165,7 @@
 }
 
 void DownloadControllerClientLacros::OnDownloadUpdated(
+    content::DownloadManager* manager,
     download::DownloadItem* item) {
   auto* service = chromeos::LacrosService::Get();
   if (!service->IsAvailable<crosapi::mojom::DownloadController>())
@@ -295,6 +176,7 @@
 }
 
 void DownloadControllerClientLacros::OnDownloadDestroyed(
+    content::DownloadManager* manager,
     download::DownloadItem* item) {
   auto* service = chromeos::LacrosService::Get();
   if (!service->IsAvailable<crosapi::mojom::DownloadController>())
diff --git a/chrome/browser/lacros/download_controller_client_lacros.h b/chrome/browser/lacros/download_controller_client_lacros.h
index 52cae56..6bf7174 100644
--- a/chrome/browser/lacros/download_controller_client_lacros.h
+++ b/chrome/browser/lacros/download_controller_client_lacros.h
@@ -5,20 +5,18 @@
 #ifndef CHROME_BROWSER_LACROS_DOWNLOAD_CONTROLLER_CLIENT_LACROS_H_
 #define CHROME_BROWSER_LACROS_DOWNLOAD_CONTROLLER_CLIENT_LACROS_H_
 
-#include <memory>
-#include <set>
 #include <string>
 
-#include "base/containers/unique_ptr_adapters.h"
-#include "base/scoped_multi_source_observation.h"
-#include "base/scoped_observation.h"
+#include "chrome/browser/download/notification/multi_profile_download_notifier.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
-#include "chrome/browser/profiles/profile_observer.h"
 #include "chromeos/crosapi/mojom/download_controller.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
+namespace content {
+class DownloadManager;
+}  // namespace content
+
 namespace download {
 class DownloadItem;
 }  // namespace download
@@ -28,8 +26,8 @@
 // calls to pause, cancel, and resume downloads from ash-chrome, hence the name.
 class DownloadControllerClientLacros
     : public crosapi::mojom::DownloadControllerClient,
-      public ProfileManagerObserver,
-      public ProfileObserver {
+      public MultiProfileDownloadNotifier::Client,
+      public ProfileManagerObserver {
  public:
   DownloadControllerClientLacros();
   DownloadControllerClientLacros(const DownloadControllerClientLacros&) =
@@ -39,8 +37,6 @@
   ~DownloadControllerClientLacros() override;
 
  private:
-  class ObservableDownloadManager;
-
   // crosapi::mojom::DownloadControllerClient:
   void GetAllDownloads(
       crosapi::mojom::DownloadControllerClient::GetAllDownloadsCallback
@@ -54,24 +50,20 @@
   // ProfileManagerObserver:
   void OnProfileAdded(Profile* profile) override;
 
-  // ProfileObserver:
-  void OnOffTheRecordProfileCreated(Profile* off_the_record) override;
-  void OnProfileWillBeDestroyed(Profile* profile) override;
-
-  void OnManagerGoingDown(ObservableDownloadManager* observable_manager);
-  void OnDownloadCreated(download::DownloadItem* item);
-  void OnDownloadUpdated(download::DownloadItem* item);
-  void OnDownloadDestroyed(download::DownloadItem* item);
-
-  std::set<std::unique_ptr<ObservableDownloadManager>,
-           base::UniquePtrComparator>
-      observable_download_managers_;
-
-  base::ScopedMultiSourceObservation<Profile, ProfileObserver>
-      profile_observer_{this};
+  // MultiProfileDownloadNotifier::Client:
+  void OnManagerInitialized(content::DownloadManager* manager) override;
+  void OnManagerGoingDown(content::DownloadManager* manager) override;
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnDownloadUpdated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnDownloadDestroyed(content::DownloadManager* manager,
+                           download::DownloadItem* item) override;
 
   mojo::Receiver<crosapi::mojom::DownloadControllerClient> client_receiver_{
       this};
+  MultiProfileDownloadNotifier download_notifier_{
+      this, /*wait_for_manager_initialization=*/true};
 };
 
 #endif  // CHROME_BROWSER_LACROS_DOWNLOAD_CONTROLLER_CLIENT_LACROS_H_
diff --git a/chrome/browser/lens/metrics/lens_metrics.h b/chrome/browser/lens/metrics/lens_metrics.h
index b7b3980..b6787c29 100644
--- a/chrome/browser/lens/metrics/lens_metrics.h
+++ b/chrome/browser/lens/metrics/lens_metrics.h
@@ -7,11 +7,22 @@
 
 namespace lens {
 
+// Histogram for recording the capture result of Lens Region Search. See enum
+// below for types of results.
 constexpr char kLensRegionSearchCaptureResultHistogramName[] =
     "Search.RegionsSearch.Lens.Result";
 
-// This should be kept in sync with the LensRegionSearchCaptureResult enum in
-// tools/metrics/histograms/enums.xml.
+// Histogram for recording the viewport proportion in relation to region
+// selected for the Lens Region Search feature.
+constexpr char kLensRegionSearchRegionViewportProportionHistogramName[] =
+    "Search.RegionSearch.Lens.RegionViewportProportion";
+
+// Histogram for recording the aspect ratio of the captured region.
+constexpr char kLensRegionSearchRegionAspectRatioHistogramName[] =
+    "Search.RegionSearch.Lens.RegionAspectRatio";
+
+// This should be kept in sync with the LensRegionSearchCaptureResult enum
+// in tools/metrics/histograms/enums.xml.
 enum class LensRegionSearchCaptureResult {
   SUCCESS = 0,
   FAILED_TO_OPEN_TAB = 1,
@@ -19,6 +30,23 @@
   kMaxValue = ERROR_CAPTURING_REGION
 };
 
+// This should be kept in sync with the LensRegionSearchAspectRatio enum
+// in tools/metrics/histograms/enums.xml. The aspect ratios are defined as:
+//  SQUARE: [0.8, 1.2]
+//  WIDE: (1.2, 1.7]
+//  VERY_WIDE: (1.7, infinity)
+//  TALL: [0.3, 0.8)
+//  VERY_TALL: [0, 0.3)
+enum class LensRegionSearchAspectRatio {
+  UNDEFINED = 0,
+  SQUARE = 1,
+  WIDE = 2,
+  VERY_WIDE = 3,
+  TALL = 4,
+  VERY_TALL = 5,
+  kMaxValue = VERY_TALL
+};
+
 }  // namespace lens
 
 #endif  // CHROME_BROWSER_LENS_METRICS_LENS_METRICS_H_
diff --git a/chrome/browser/lens/region_search/lens_region_search_controller.cc b/chrome/browser/lens/region_search/lens_region_search_controller.cc
index ff8b309..f2d6ece4 100644
--- a/chrome/browser/lens/region_search/lens_region_search_controller.cc
+++ b/chrome/browser/lens/region_search/lens_region_search_controller.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 #include "chrome/browser/lens/region_search/lens_region_search_controller.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/image_editor/screenshot_flow.h"
 #include "chrome/browser/lens/metrics/lens_metrics.h"
@@ -52,6 +53,77 @@
                             result);
 }
 
+int LensRegionSearchController::CalculateViewportProportionFromAreas(
+    int screen_height,
+    int screen_width,
+    int image_width,
+    int image_height) {
+  // To get the region proportion of the screen, we must calculate the areas of
+  // the screen and captured region. Then, we must divide the area of the region
+  // by the area of the screen to get the percentage. Multiply by 100 to make it
+  // an integer. Returns -1 if screen_area is 0 to prevent undefined values.
+  double screen_area = screen_width * screen_height;
+  if (screen_area <= 0) {
+    return -1;
+  }
+  double region_area = image_width * image_height;
+  double region_proportion = region_area / screen_area;
+  int region_proportion_int = region_proportion * 100;
+  return region_proportion_int;
+}
+
+lens::LensRegionSearchAspectRatio
+LensRegionSearchController::GetAspectRatioFromSize(int image_height,
+                                                   int image_width) {
+  // Convert to double to prevent integer division.
+  double width = image_width;
+  double height = image_height;
+
+  // To record region aspect ratio, we must divide the region's width by height.
+  // Should never be zero, but check to prevent any crashes or undefined
+  // recordings.
+  if (height > 0) {
+    double aspect_ratio = width / height;
+    if (aspect_ratio <= 1.2 && aspect_ratio >= 0.8) {
+      return lens::LensRegionSearchAspectRatio::SQUARE;
+    } else if (aspect_ratio < 0.8 && aspect_ratio >= 0.3) {
+      return lens::LensRegionSearchAspectRatio::TALL;
+    } else if (aspect_ratio < 0.3) {
+      return lens::LensRegionSearchAspectRatio::VERY_TALL;
+    } else if (aspect_ratio > 1.2 && aspect_ratio <= 1.7) {
+      return lens::LensRegionSearchAspectRatio::WIDE;
+    } else if (aspect_ratio > 1.7) {
+      return lens::LensRegionSearchAspectRatio::VERY_WIDE;
+    }
+  }
+  return lens::LensRegionSearchAspectRatio::UNDEFINED;
+}
+
+void LensRegionSearchController::RecordRegionSizeRelatedMetrics(
+    gfx::Rect screen_bounds,
+    gfx::Size image_size) {
+  // If any of the rects are empty, it means the area is zero. In this case,
+  // return.
+  if (screen_bounds.IsEmpty() || image_size.IsEmpty())
+    return;
+  double region_width = image_size.width();
+  double region_height = image_size.height();
+
+  int region_proportion = CalculateViewportProportionFromAreas(
+      screen_bounds.height(), screen_bounds.width(), region_width,
+      region_height);
+  if (region_proportion >= 0) {
+    base::UmaHistogramPercentage(
+        lens::kLensRegionSearchRegionViewportProportionHistogramName,
+        region_proportion);
+  }
+
+  // To record region aspect ratio, we must divide the region's width by height.
+  base::UmaHistogramEnumeration(
+      lens::kLensRegionSearchRegionAspectRatioHistogramName,
+      GetAspectRatioFromSize(region_height, region_width));
+}
+
 void LensRegionSearchController::OnCaptureCompleted(
     const image_editor::ScreenshotCaptureResult& result) {
   const gfx::Image& captured_image = result.image;
@@ -62,6 +134,9 @@
     return;
   }
 
+  // Record region size related UMA histograms according to region and screen.
+  RecordRegionSizeRelatedMetrics(result.screen_bounds, captured_image.Size());
+
   const gfx::Image& image = ResizeImageIfNecessary(captured_image);
   CoreTabHelper* core_tab_helper =
       CoreTabHelper::FromWebContents(source_web_contents_);
@@ -71,7 +146,8 @@
     return;
   }
   core_tab_helper->SearchWithLensInNewTab(
-      image, lens::EntryPoint::CHROME_REGION_SEARCH_MENU_ITEM);
+      image, captured_image.Size(),
+      lens::EntryPoint::CHROME_REGION_SEARCH_MENU_ITEM);
   RecordCaptureResult(lens::LensRegionSearchCaptureResult::SUCCESS);
 }
 
diff --git a/chrome/browser/lens/region_search/lens_region_search_controller.h b/chrome/browser/lens/region_search/lens_region_search_controller.h
index 72866c1..bbd6ca5 100644
--- a/chrome/browser/lens/region_search/lens_region_search_controller.h
+++ b/chrome/browser/lens/region_search/lens_region_search_controller.h
@@ -7,6 +7,7 @@
 #include "chrome/browser/image_editor/screenshot_flow.h"
 #include "chrome/browser/lens/metrics/lens_metrics.h"
 #include "content/public/browser/web_contents.h"
+
 namespace lens {
 
 class LensRegionSearchController {
@@ -19,9 +20,22 @@
   // around the web contents. When finished with selection, the region is
   // converted into a PNG and sent to Lens.
   void Start();
+  // Calculates the percentage that the image area takes up in the screen area.
+  // This value is calculated as double and then floored to the nearest integer.
+  static int CalculateViewportProportionFromAreas(int screen_height,
+                                                  int screen_width,
+                                                  int image_width,
+                                                  int image_height);
+  // Returns an enum representing the aspect ratio of the image as defined in
+  // lens_metrics.h.
+  static lens::LensRegionSearchAspectRatio GetAspectRatioFromSize(
+      int image_height,
+      int image_width);
 
  private:
   void RecordCaptureResult(lens::LensRegionSearchCaptureResult result);
+  void RecordRegionSizeRelatedMetrics(gfx::Rect screen_bounds,
+                                      gfx::Size region_size);
 
   void OnCaptureCompleted(const image_editor::ScreenshotCaptureResult& result);
   gfx::Image ResizeImageIfNecessary(const gfx::Image& image);
diff --git a/chrome/browser/lens/region_search/lens_region_search_controller_unittest.cc b/chrome/browser/lens/region_search/lens_region_search_controller_unittest.cc
new file mode 100644
index 0000000..6900fbcc
--- /dev/null
+++ b/chrome/browser/lens/region_search/lens_region_search_controller_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "chrome/browser/lens/region_search/lens_region_search_controller.h"
+
+#include "chrome/browser/lens/metrics/lens_metrics.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace lens {
+
+TEST(LensRegionSearchControllerTest, UndefinedAspectRatioTest) {
+  int height = 0;
+  int width = 100;
+  EXPECT_EQ(LensRegionSearchController::GetAspectRatioFromSize(height, width),
+            LensRegionSearchAspectRatio::UNDEFINED);
+}
+
+TEST(LensRegionSearchControllerTest, SquareAspectRatioTest) {
+  int height = 100;
+  int width = 100;
+  EXPECT_EQ(LensRegionSearchController::GetAspectRatioFromSize(height, width),
+            LensRegionSearchAspectRatio::SQUARE);
+}
+
+TEST(LensRegionSearchControllerTest, WideAspectRatioTest) {
+  int height = 100;
+  int width = 170;
+  EXPECT_EQ(LensRegionSearchController::GetAspectRatioFromSize(height, width),
+            LensRegionSearchAspectRatio::WIDE);
+}
+
+TEST(LensRegionSearchControllerTest, VeryWideAspectRatioTest) {
+  int height = 100;
+  int width = 10000;
+  EXPECT_EQ(LensRegionSearchController::GetAspectRatioFromSize(height, width),
+            LensRegionSearchAspectRatio::VERY_WIDE);
+}
+
+TEST(LensRegionSearchControllerTest, TallAspectRatioTest) {
+  int height = 170;
+  int width = 100;
+  EXPECT_EQ(LensRegionSearchController::GetAspectRatioFromSize(height, width),
+            LensRegionSearchAspectRatio::TALL);
+}
+
+TEST(LensRegionSearchControllerTest, VeryTallAspectRatioTest) {
+  int height = 10000;
+  int width = 100;
+  EXPECT_EQ(LensRegionSearchController::GetAspectRatioFromSize(height, width),
+            LensRegionSearchAspectRatio::VERY_TALL);
+}
+
+TEST(LensRegionSearchControllerTest, AccurateViewportProportionTest) {
+  int screen_height = 1000;
+  int screen_width = 1000;
+  int image_height = 100;
+  int image_width = 100;
+  EXPECT_EQ(LensRegionSearchController::CalculateViewportProportionFromAreas(
+                screen_height, screen_width, image_width, image_height),
+            1);
+}
+
+TEST(LensRegionSearchControllerTest, UndefinedViewportProportionTest) {
+  int screen_height = 0;
+  int screen_width = 0;
+  int image_height = 100;
+  int image_width = 100;
+  EXPECT_EQ(LensRegionSearchController::CalculateViewportProportionFromAreas(
+                screen_height, screen_width, image_width, image_height),
+            -1);
+}
+
+}  // namespace lens
diff --git a/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc b/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc
index 2d853e13..a4f02cc 100644
--- a/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc
+++ b/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc
@@ -10,6 +10,7 @@
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/policy/messaging_layer/upload/record_upload_request_builder.h"
 #include "components/reporting/proto/record.pb.h"
 #include "components/reporting/proto/record_constants.pb.h"
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc b/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc
index 134ed17..02dc0ed 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_unittest.cc
@@ -118,10 +118,10 @@
                     ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE));
     EXPECT_TRUE(content_type->SupportsGroup(
                     ContextMenuContentType::ITEM_GROUP_PRINT));
-    EXPECT_TRUE(content_type->SupportsGroup(
-        ContextMenuContentType::ITEM_GROUP_LENS_REGION_SEARCH));
 
     EXPECT_FALSE(content_type->SupportsGroup(
+        ContextMenuContentType::ITEM_GROUP_LENS_REGION_SEARCH));
+    EXPECT_FALSE(content_type->SupportsGroup(
                     ContextMenuContentType::ITEM_GROUP_MEDIA_VIDEO));
     EXPECT_FALSE(content_type->SupportsGroup(
                     ContextMenuContentType::ITEM_GROUP_MEDIA_AUDIO));
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 6da0c74..9e358b1c 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -875,10 +875,8 @@
   if (media_image)
     AppendImageItems();
 
-  // Do not show image search menu items if Lens Region Search will be shown.
   if (content_type_->SupportsGroup(
-          ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE) &&
-      !IsLensRegionSearchEnabled()) {
+          ContextMenuContentType::ITEM_GROUP_SEARCHWEBFORIMAGE)) {
     if (base::FeatureList::IsEnabled(lens::features::kLensStandalone) &&
         search::DefaultSearchProviderIsGoogle(GetProfile())) {
       AppendSearchLensForImageItems();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index 050fc24..46cc12af 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -734,6 +734,22 @@
 
   EXPECT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH));
 }
+
+// Verify that the Lens Region Search menu item is disabled when the user
+// clicks on an image.
+TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchDisabledOnImage) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  SetUserSelectedDefaultSearchProvider("https://www.google.com");
+  content::ContextMenuParams params = CreateParams(MenuItem::IMAGE);
+  params.has_image_contents = true;
+  auto menu = std::make_unique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
+  AppendImageItems(menu.get());
+
+  EXPECT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH));
+}
+
 // Verify that the Lens Region Search menu item is disabled when the user's
 // default browser is not Google.
 TEST_F(RenderViewContextMenuPrefsTest,
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
index 2ec88747..3a2601b8 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
@@ -115,6 +115,7 @@
     "nodes/group_node_test.js",
     "nodes/tab_node_test.js",
     "point_scan_manager_test.js",
+    "saatlite/gen/saatlite_tests.js",
     "switch_access_predicate_test.js",
     "switch_access_test.js",
     "text_navigation_manager_test.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compile_saatlite_tests.py b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compile_saatlite_tests.py
new file mode 100755
index 0000000..cf71533
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compile_saatlite_tests.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+# 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.
+'''Compiles all tests in the tests/ directory from Switch Access Automated
+Testing Language into a js2gtest.'''
+
+import os
+import sys
+
+def main() -> None:
+  SAATL_dir = os.path.dirname(__file__)
+  compiler_dir = os.path.abspath(os.path.join(SAATL_dir, 'compiler/'))
+  test_dir = os.path.abspath(os.path.join(SAATL_dir, 'tests/')) + '/'
+  out_file = os.path.abspath(os.path.join(SAATL_dir, 'gen/saatlite_tests.js'))
+
+  initial_dir = os.getcwd()
+  os.chdir(compiler_dir)
+
+  args = ['node',
+          'compiler.js',
+          test_dir,
+          out_file]
+
+  os.system(' '.join(args))
+  os.chdir(initial_dir)
+
+if __name__ == '__main__':
+  main()
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/Makefile b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/Makefile
new file mode 100644
index 0000000..5ebd129
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/Makefile
@@ -0,0 +1,6 @@
+parser: parser.jison lexer.jisonlex
+	npx jison-gho parser.jison lexer.jisonlex -o parser.js
+	./format_parser.sh
+
+clean:
+	rm -f parser.js
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/README b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/README
new file mode 100644
index 0000000..56d459f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/README
@@ -0,0 +1,10 @@
+# Instructions to Rebuild the Parser
+
+After making changes to the parser.jison or lexer.jisonlex files, follow the
+below instructions to rebuild the parser.js module.
+
+1. Install node (`apt install nodejs`)
+2. Install npm (`apt install npm`)
+3. Install npx (`npm install npx`)
+4. (Optional) Ensure the old parser is removed (`make clean`)
+5. Build the parser (`make parser`)
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js
new file mode 100644
index 0000000..5144cc6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js
@@ -0,0 +1,62 @@
+// 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.
+
+/*
+ * Main entry point for the SAATLite compiler. It handles reading the .saatl
+ * in files, calling the parser, and writing the .js out files.
+ */
+
+const parse = require('./parser').parse;
+const fs = require('fs');
+
+const preamble = `// 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.
+
+GEN_INCLUDE(['../../switch_access_e2e_test_base.js', '../../test_utility.js']);
+
+/** Test fixture for the SAATLite generated tests. */
+SwitchAccessSAATLiteTest = class extends SwitchAccessE2ETest {
+  /** @override */
+  setUp() {
+    const runTest = this.deferRunTest(WhenTestDone.EXPECT);
+    (async () => {
+      await TestUtility.setup();
+      runTest();
+    })();
+  }
+};
+
+`;
+
+const args = process.argv.slice(2);
+const testDir = args[0];
+const outFile = args[1];
+
+if (typeof testDir !== 'string' || typeof outFile !== 'string') {
+  throw new Error(
+      'Error: compiler needs two string arguments: ' +
+      'the test dir and the out file.');
+}
+
+// Delete the output file if it already exists.
+if (fs.existsSync(outFile)) {
+  fs.unlinkSync(outFile);
+}
+
+const stream = fs.createWriteStream(outFile);
+stream.on('error', console.error);
+stream.on('open', () => {
+  stream.write(preamble);
+
+  // Read all the files in the tests/ directory.
+  const filenames = fs.readdirSync(testDir);
+  filenames.forEach((filename) => {
+    console.log('Compiling file: ', filename);
+    const contents = fs.readFileSync(testDir + filename, {encoding: 'utf8'});
+    stream.write(parse(contents).output + '\n');
+  });
+
+  stream.end();
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/format_parser.sh b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/format_parser.sh
new file mode 100755
index 0000000..c5b010a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/format_parser.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# 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.
+
+# This file formats the auto-generated parser.js so it passes presubmit checks.
+
+if ! grep -q 'Copyright' parser.js; then
+  sed -i '0,/\//{s|/|// Copyright 2021 The Chromium Authors. Al#\n/|}' parser.js
+  sed -i '0,/#/{s|#|l rights reserved.\n// Use of this source code#|}' parser.js
+  sed -i '0,/#/{s|#| is governed by a BSD-style license that can b#|}' parser.js
+  sed -i '0,/#/{s|#|e\n// found in the LICENSE file.\n|}' parser.js
+fi
+
+sed -i 's/show_input_position == undefined/!show_input_position/g' parser.js
+sed -i "s|[ \t\n]*// can't ever have more input lines than this![ \t\n]*| |g" \
+  parser.js
+
+sed -i "s/recovery approach availabl/recovery approach '+'availabl/g" parser.js
+sed -i "s/the lexer is of/the '+'lexer is of/g" parser.js
+sed -i "s/persuasion (options./persuasion '+'(options./g" parser.js
+sed -i "s/non-existing condition/non-existing '+'condition/g" parser.js
+sed -i "s/the application programmer/the '+'application programmer/g" parser.js
+
+sed -i 's/preceeding/preceding/g' parser.js
+
+sed -i 's/sharedState_yy/sharedStateYy/g' parser.js
+sed -i 's/this_production/thisProduction/g' parser.js
+sed -i 's/pretty_src/prettySrc/g' parser.js
+sed -i 's/pos_str/posStr/g' parser.js
+sed -i 's/lineno_msg/linenoMsg/g' parser.js
+sed -i 's/rule_re/ruleRe/g' parser.js
+sed -i 's/rule_ids/ruleIds/g' parser.js
+sed -i 's/rule_regexes/ruleRegexes/g' parser.js
+sed -i 's/rule_new_ids/ruleNewIds/g' parser.js
+sed -i 's/slice_len/sliceLen/g' parser.js
+sed -i 's/pre_lines/preLines/g' parser.js
+sed -i 's/lineno_display_width/linenoDisplayWidth/g' parser.js
+sed -i 's/ws_prefix/wsPrefix/g' parser.js
+sed -i 's/nonempty_line_indexes/nonemptyLineIndexes/g' parser.js
+sed -i 's/lno_pfx/lnoPfx/g' parser.js
+sed -i 's/clip_start/clipStart/g' parser.js
+sed -i 's/clip_end/clipEnd/g' parser.js
+sed -i 's/intermediate_line/intermediateLine/g' parser.js
+sed -i 's/yy_/yY/g' parser.js
+sed -i 's/MINIMUM_VISIBLE_NONEMPTY_LINE_COUNT/MIN_VIS_LINE/g' parser.js
+
+git cl format --js parser.js
+
+node_dir='../../../../../../../../third_party/node'
+
+${node_dir}/linux/node-linux-x64/bin/node \
+  ${node_dir}/node_modules/eslint/bin/eslint \
+  --resolve-plugins-relative-to ${node_dir}/node_modules \
+  --ignore-pattern .eslintrc.js parser.js --fix
+
+git cl format --js parser.js
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/lexer.jisonlex b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/lexer.jisonlex
new file mode 100644
index 0000000..0429201
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/lexer.jisonlex
@@ -0,0 +1,33 @@
+// 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.
+
+%options case-insensitive
+
+%%
+[<][^\n]+                       return 'HTML_SNIPPET';
+chrome[:][/][/][^\n]*           return 'CHROME_URL';
+\"[^"]*\"                       return 'STRING_LITERAL';
+
+\n                              return 'EOL';
+[0-9]+                          return 'NUMBER';
+[(]                             return 'LEFT_PARENS';
+[)]                             return 'RIGHT_PARENS';
+[,]                             return 'COMMA';
+
+expect                          return 'EXPECT';
+focus                           return 'FOCUS';
+load[ ]page                     return 'LOAD_PAGE';
+next                            return 'NEXT';
+on                              return 'ON';
+previous                        return 'PREVIOUS';
+select                          return 'SELECT';
+
+button                          return 'ROLE';
+slider                          return 'ROLE';
+spinButton                      return 'ROLE';
+textField                       return 'ROLE';
+textFieldWithComboBox           return 'ROLE';
+
+<<EOF>>                         return 'EOF';
+\s                              /* Ignore whitespace */;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/package.json b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/package.json
new file mode 100644
index 0000000..e6b892a3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "saatlite_compiler",
+  "version": "1.0.0",
+  "description": "Compiles SAATLite simple declarative tests into extension js2gtest format.",
+  "main": "compiler.js",
+  "scripts": {
+    "compiler.js": "node compiler.js"
+  },
+  "author": "The Chromium Authors",
+  "license": "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."
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.jison b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.jison
new file mode 100644
index 0000000..9b5a6b6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.jison
@@ -0,0 +1,212 @@
+// 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.
+
+/* This file compiles SAATLite tests into JS tests. */
+
+// Preamble:
+%{
+  let page, point, objectToMatch;
+
+  let indent = '';
+  const increaseIndent = () => indent = indent + '  ';
+  const decreaseIndent = () => indent = indent.substring(2);
+
+  let buffer = '';
+  const addToBuffer = (text) => buffer += indent + text + '\n';
+  const flushBuffer = () => {
+    const result = buffer;
+    buffer = '';
+    return result;
+  }
+
+  function addAllStatements(statements) {
+    if (statements.find(statement => !statement.output)) {
+      throw new Error('Compiler error: statement not converted to Javascript');
+    }
+    statements.forEach(statement => addToBuffer(statement.output));
+    return flushBuffer();
+  }
+
+  // Initialize test before anything else.
+  const initTest = (page = `''`) => {
+    addToBuffer(`TEST_F('SwitchAccessSAATLiteTest', 'Demo', function() {`);
+    increaseIndent();
+    addToBuffer(`this.runWithLoadedTree(${page.output}, async (rootWebArea) => {`);
+    increaseIndent();
+    addToBuffer('TestUtility.startFocusInside(rootWebArea);');
+    return flushBuffer();
+  }
+
+  const finishTest = (opt_url) => {
+    decreaseIndent();
+    if (opt_url) {
+      addToBuffer(`}, {url: ${opt_url.output}});`);
+    } else {
+      addToBuffer(`});`);
+    }
+    decreaseIndent();
+    addToBuffer(`});`);
+
+    return flushBuffer();
+  }
+%}
+
+%start test
+
+%%
+
+test
+  : load_expression statements EOF {
+      $$ = {};
+      if ($1.page.type === 'HTML') {
+        $$.output = initTest($1.page);
+      } else {
+        $$.output = initTest();
+      }
+
+      $$.output += addAllStatements($2);
+
+      if ($1.page.type === 'ChromeURL') {
+        $$.output += finishTest($1.page);
+      } else {
+        $$.output += finishTest();
+      }
+
+      $2.unshift($1);
+      $$.ast = $2;
+      return $$;
+    }
+  | statements EOF {
+      $$ = {ast: $1};
+      $$.output = initTest();
+      $$.output += addAllStatements($1);
+      $$.output += finishTest();
+
+      return $$;
+    }
+  ;
+
+load_expression
+  : LOAD_PAGE page_expression {
+      $$ = {command: 'Load', page: $2};
+    }
+  | LOAD_PAGE EOL page_expression {
+      $$ = {command: 'Load', page: $3};
+    }
+  ;
+
+statements
+  // Expect a new line between statements.
+  : statements EOL statement {
+      $$ = $1;
+      $$.push($3);
+    }
+  // Allow arbitrary empty lines.
+  | statements EOL {
+      $$ = $1;
+    }
+  | statement {
+      $$ = [$1];
+    }
+  | /* Empty */ {
+      $$ = [];
+    }
+  ;
+
+statement
+  : NEXT {
+      $$ = {command: 'Next'};
+      $$.output = 'TestUtility.pressNextSwitch();';
+    }
+  | PREVIOUS {
+      $$ = {command: 'Previous'};
+      $$.output = 'TestUtility.pressPreviousSwitch();';
+    }
+  | SELECT point_expression {
+      $$ = {command: 'Select'};
+      // A point_expression is only expected when point_scan is enabled.
+      if ($2) {
+        $$.point = $2;
+        point = $$.point.output;
+        $$.output = `TestUtility.simulatePointScanSelect(${point});`;
+      } else {
+        $$.output = 'TestUtility.pressSelectSwitch();';
+      }
+    }
+  | EXPECT expectation_expression {
+      $$ = {command: 'Expect', expectation: $2};
+      $$.output = $2.output;
+    }
+  ;
+
+page_expression
+  : html_page {
+      $$ = {type: 'HTML', value: $1};
+      page = $$.value;
+      $$.output = `'` + page + `'`;
+    }
+  | CHROME_URL {
+      $$ = {type: 'ChromeURL', value: $1};
+      page = $$.value;
+      $$.output = `'` + page + `'`;
+    }
+  ;
+
+html_page
+  : html_page HTML_SNIPPET {
+      $$ = $1 + '\n' + $2;
+    }
+  | HTML_SNIPPET {
+      $$ = $1;
+    }
+  ;
+
+point_expression
+  : LEFT_PARENS NUMBER COMMA NUMBER RIGHT_PARENS {
+      $$ = {x: $2, y: $4};
+      point = $$;
+      $$.output = `{x: ${point.x}, y: ${point.y}}`;
+    }
+  | /* Empty */ {
+      $$ = null;
+    }
+  ;
+
+expectation_expression
+  : FOCUS ON focusable_expression {
+      $$ = {type: 'Focus', matches: $3};
+      objectToMatch = $$.matches.output;
+      $$.output = `await TestUtility.expectFocusOn(${objectToMatch});`;
+    }
+  ;
+
+focusable_expression
+  : ROLE string_literal {
+      $$ = {role: $1, name: $2};
+      objectToMatch = $$;
+      $$.output = `{role: '${objectToMatch.role}', name: '${objectToMatch.name}'}`;
+    }
+  | string_literal ROLE {
+      $$ = {role: $2, name: $1};
+      objectToMatch = $$;
+      $$.output = `{role: '${objectToMatch.role}', name: '${objectToMatch.name}'}`;
+    }
+  | ROLE {
+      $$ = {role: $1};
+      objectToMatch = $$;
+      $$.output = `{role: '${objectToMatch.role}'}`;
+    }
+  | string_literal {
+      $$ = {name: $1};
+      objectToMatch = $$;
+      $$.output = `{name: '${objectToMatch.name}'}`;
+    }
+  ;
+
+string_literal
+  : STRING_LITERAL {
+      // Remove the quotes from the string.
+      $$ = $1.substring(1, $1.length - 1);
+    }
+  ;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js
new file mode 100644
index 0000000..460598654
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js
@@ -0,0 +1,3582 @@
+
+// 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.
+
+/* parser generated by jison 0.6.1-215 */
+
+/*
+ * Returns a Parser object of the following structure:
+ *
+ *  Parser: {
+ *    yy: {}     The so-called "shared state" or rather the *source* of it;
+ *               the real "shared state" `yy` passed around to
+ *               the rule actions, etc. is a derivative/copy of this one,
+ *               not a direct reference!
+ *  }
+ *
+ *  Parser.prototype: {
+ *    yy: {},
+ *    EOF: 1,
+ *    TERROR: 2,
+ *
+ *    trace: function(errorMessage, ...),
+ *
+ *    JisonParserError: function(msg, hash),
+ *
+ *    quoteName: function(name),
+ *               Helper function which can be overridden by user code later on:
+ * put suitable quotes around literal IDs in a description string.
+ *
+ *    originalQuoteName: function(name),
+ *               The basic quoteName handler provided by JISON.
+ *               `cleanupAfterParse()` will clean up and reset `quoteName()` to
+ * reference this function at the end of the `parse()`.
+ *
+ *    describeSymbol: function(symbol),
+ *               Return a more-or-less human-readable description of the given
+ * symbol, when available, or the symbol itself, serving as its own
+ * 'description' for lack of something better to serve up.
+ *
+ *               Return NULL when the symbol is unknown to the parser.
+ *
+ *    symbols_: {associative list: name ==> number},
+ *    terminals_: {associative list: number ==> name},
+ *    nonterminals: {associative list: rule-name ==> {associative list: number
+ * ==> rule-alt}}, terminal_descriptions_: (if there are any) {associative list:
+ * number ==> description}, productions_: [...],
+ *
+ *    performAction: function parser__performAction(yytext, yyleng, yylineno,
+ * yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack),
+ *
+ *               The function parameters and `this` have the following
+ * value/meaning:
+ *               - `this`    : reference to the `yyval` internal object, which
+ * has members (`$` and `_$`) to store/reference the rule value `$$` and
+ * location info `@$`.
+ *
+ *                 One important thing to note about `this` a.k.a. `yyval`:
+ * every *reduce* action gets to see the same object via the `this` reference,
+ * i.e. if you wish to carry custom data from one reduce action through to the
+ * next within a single parse run, then you may get nasty and use `yyval` a.k.a.
+ * `this` for storing you own semi-permanent data.
+ *
+ *                 `this.yy` is a direct reference to the `yy` shared state
+ * object.
+ *
+ *                 `%parse-param`-specified additional `parse()` arguments have
+ * been added to this `yy` object at `parse()` start and are therefore available
+ * to the action code via the same named `yy.xxxx` attributes (where `xxxx`
+ * represents a identifier name from the %parse-param` list.
+ *
+ *               - `yytext`  : reference to the lexer value which belongs to the
+ * last lexer token used to match this rule. This is *not* the look-ahead token,
+ * but the last token that's actually part of this rule.
+ *
+ *                 Formulated another way, `yytext` is the value of the token
+ * immediately preceding the current look-ahead token. Caveats apply for rules
+ * which don't require look-ahead, such as epsilon rules.
+ *
+ *               - `yyleng`  : ditto as `yytext`, only now for the lexer.yyleng
+ * value.
+ *
+ *               - `yylineno`: ditto as `yytext`, only now for the
+ * lexer.yylineno value.
+ *
+ *               - `yyloc`   : ditto as `yytext`, only now for the lexer.yylloc
+ * lexer token location info.
+ *
+ *                               WARNING: since jison 0.4.18-186 this entry may
+ * be NULL/UNDEFINED instead of an empty object when no suitable location info
+ * can be provided.
+ *
+ *               - `yystate` : the current parser state number, used internally
+ * for dispatching and executing the action code chunk matching the rule
+ * currently being reduced.
+ *
+ *               - `yysp`    : the current state stack position (a.k.a. 'stack
+ * pointer')
+ *
+ *                 This one comes in handy when you are going to do advanced
+ * things to the parser stacks, all of which are accessible from your action
+ * code (see the next entries below).
+ *
+ *                 Also note that you can access this and other stack index
+ * values using the new double-hash syntax, i.e. `##$ === ##0 === yysp`, while
+ * `##1` is the stack index for all things related to the first rule term, just
+ * like you have `$1`, `@1` and `#1`. This is made available to write very
+ * advanced grammar action rules, e.g. when you want to investigate the parse
+ * state stack in your action code, which would, for example, be relevant when
+ * you wish to implement error diagnostics and reporting schemes similar to the
+ * work described here:
+ *
+ *                 + Pottier, F., 2016. Reachability and error diagnosis in
+ * LR(1) automata. In Journées Francophones des Languages Applicatifs.
+ *
+ *                 + Jeffery, C.L., 2003. Generating LR syntax error messages
+ * from examples. ACM Transactions on Programming Languages and Systems
+ * (TOPLAS), 25(5), pp.631–640.
+ *
+ *               - `yyrulelength`: the current rule's term count, i.e. the
+ * number of entries occupied on the stack.
+ *
+ *                 This one comes in handy when you are going to do advanced
+ * things to the parser stacks, all of which are accessible from your action
+ * code (see the next entries below).
+ *
+ *               - `yyvstack`: reference to the parser value stack. Also
+ * accessed via the `$1` etc. constructs.
+ *
+ *               - `yylstack`: reference to the parser token location stack.
+ * Also accessed via the `@1` etc. constructs.
+ *
+ *                             WARNING: since jison 0.4.18-186 this array MAY
+ * contain slots which are UNDEFINED rather than an empty (location) object,
+ * when the lexer/parser action code did not provide a suitable location info
+ * object when such a slot was filled!
+ *
+ *               - `yystack` : reference to the parser token id stack. Also
+ * accessed via the
+ *                             `#1` etc. constructs.
+ *
+ *                 Note: this is a bit of a **white lie** as we can statically
+ * decode any `#n` reference to its numeric token id value, hence that code
+ * wouldn't need the `yystack` but *you* might want access this array for your
+ * own purposes, such as error analysis as mentioned above!
+ *
+ *                 Note that this stack stores the current stack of *tokens*,
+ * that is the sequence of already parsed=reduced *nonterminals* (tokens
+ * representing rules) and *terminals* (lexer tokens *shifted* onto the stack
+ * until the rule they belong to is found and *reduced*.
+ *
+ *               - `yysstack`: reference to the parser state stack. This one
+ * carries the internal parser *states* such as the one in `yystate`, which are
+ * used to represent the parser state machine in the *parse table*. *Very*
+ * *internal* stuff, what can I say? If you access this one, you're clearly
+ * doing wicked things
+ *
+ *               - `...`     : the extra arguments you specified in the
+ * `%parse-param` statement in your grammar definition file.
+ *
+ *    table: [...],
+ *               State transition table
+ *               ----------------------
+ *
+ *               index levels are:
+ *               - `state`  --> hash table
+ *               - `symbol` --> action (number or array)
+ *
+ *                 If the `action` is an array, these are the elements' meaning:
+ *                 - index [0]: 1 = shift, 2 = reduce, 3 = accept
+ *                 - index [1]: GOTO `state`
+ *
+ *                 If the `action` is a number, it is the GOTO `state`
+ *
+ *    defaultActions: {...},
+ *
+ *    parseError: function(str, hash, ExceptionClass),
+ *    yyError: function(str, ...),
+ *    yyRecovering: function(),
+ *    yyErrOk: function(),
+ *    yyClearIn: function(),
+ *
+ *    constructParseErrorInfo: function(error_message, exception_object,
+ * expected_token_set, is_recoverable), Helper function **which will be set up
+ * during the first invocation of the `parse()` method**. Produces a new
+ * errorInfo 'hash object' which can be passed into `parseError()`. See it's use
+ * in this parser kernel in many places; example usage:
+ *
+ *                   var infoObj = parser.constructParseErrorInfo('fail!', null,
+ *                                     parser.collect_expected_token_set(state),
+ * true); var retVal = parser.parseError(infoObj.errStr, infoObj,
+ * parser.JisonParserError);
+ *
+ *    originalParseError: function(str, hash, ExceptionClass),
+ *               The basic `parseError` handler provided by JISON.
+ *               `cleanupAfterParse()` will clean up and reset `parseError()` to
+ * reference this function at the end of the `parse()`.
+ *
+ *    options: { ... parser %options ... },
+ *
+ *    parse: function(input[, args...]),
+ *               Parse the given `input` and return the parsed value (or `true`
+ * when none was provided by the root action, in which case the parser is acting
+ * as a *matcher*). You MAY use the additional `args...` parameters as per
+ * `%parse-param` spec of this grammar: these extra `args...` are added verbatim
+ * to the `yy` object reference as member variables.
+ *
+ *               WARNING:
+ *               Parser's additional `args...` parameters (via `%parse-param`)
+ * MAY conflict with any attributes already added to `yy` by the jison run-time;
+ *               when such a collision is detected an exception is thrown to
+ * prevent the generated run-time from silently accepting this confusing and
+ * potentially hazardous situation!
+ *
+ *               The lexer MAY add its own set of additional parameters (via the
+ * `%parse-param` line in the lexer section of the grammar spec): these will be
+ * inserted in the `yy` shared state object and any collision with those will be
+ * reported by the lexer via a thrown exception.
+ *
+ *    cleanupAfterParse: function(resultValue, invoke_post_methods,
+ * do_not_nuke_errorinfos), Helper function **which will be set up during the
+ * first invocation of the `parse()` method**. This helper API is invoked at the
+ * end of the `parse()` call, unless an exception was thrown and `%options
+ * no-try-catch` has been defined for this grammar: in that case this helper MAY
+ *               be invoked by calling user code to ensure the `post_parse`
+ * callbacks are invoked and the internal parser gets properly garbage collected
+ * under these particular circumstances.
+ *
+ *    yyMergeLocationInfo: function(first_index, last_index, first_yylloc,
+ * last_yylloc, dont_look_back), Helper function **which will be set up during
+ * the first invocation of the `parse()` method**. This helper API can be
+ * invoked to calculate a spanning `yylloc` location info object.
+ *
+ *               Note: %epsilon rules MAY specify no `first_index` and
+ * `first_yylloc`, in which case this function will attempt to obtain a suitable
+ * location marker by inspecting the location stack backwards.
+ *
+ *               For more info see the documentation comment further below,
+ * immediately above this function's implementation.
+ *
+ *    lexer: {
+ *        yy: {...},           A reference to the so-called "shared state" `yy`
+ * once received via a call to the `.setInput(input, yy)` lexer API. EOF: 1,
+ *        ERROR: 2,
+ *        JisonLexerError: function(msg, hash),
+ *        parseError: function(str, hash, ExceptionClass),
+ *        setInput: function(input, [yy]),
+ *        input: function(),
+ *        unput: function(str),
+ *        more: function(),
+ *        reject: function(),
+ *        less: function(n),
+ *        pastInput: function(n),
+ *        upcomingInput: function(n),
+ *        showPosition: function(),
+ *        test_match: function(regex_match_array, rule_index, ...),
+ *        next: function(...),
+ *        lex: function(...),
+ *        begin: function(condition),
+ *        pushState: function(condition),
+ *        popState: function(),
+ *        topState: function(),
+ *        _currentRules: function(),
+ *        stateStackSize: function(),
+ *        cleanupAfterLex: function()
+ *
+ *        options: { ... lexer %options ... },
+ *
+ *        performAction: function(yy, yY, $avoiding_name_collisions, YY_START,
+ * ...), rules: [...], conditions: {associative list: name ==> set},
+ *    }
+ *  }
+ *
+ *
+ *  token location info (@$, _$, etc.): {
+ *    first_line: n,
+ *    last_line: n,
+ *    first_column: n,
+ *    last_column: n,
+ *    range: [start_number, end_number]
+ *               (where the numbers are indexes into the input string,
+ * zero-based)
+ *  }
+ *
+ * ---
+ *
+ * The `parseError` function receives a 'hash' object with these members for
+ * lexer and parser errors:
+ *
+ *  {
+ *    text:        (matched text)
+ *    token:       (the produced terminal token, if any)
+ *    token_id:    (the produced terminal token numeric ID, if any)
+ *    line:        (yylineno)
+ *    loc:         (yylloc)
+ *  }
+ *
+ * parser (grammar) errors will also provide these additional members:
+ *
+ *  {
+ *    expected:    (array describing the set of expected tokens;
+ *                  may be UNDEFINED when we cannot easily produce such a set)
+ *    state:       (integer (or array when the table includes grammar
+ * collisions); represents the current internal state of the parser kernel. can,
+ * for example, be used to pass to the `collect_expected_token_set()` API to
+ * obtain the expected token set) action:      (integer; represents the current
+ * internal action which will be executed) new_state:   (integer; represents the
+ * next/planned internal state, once the current action has executed)
+ *    recoverable: (boolean: TRUE when the parser MAY have an error recovery
+ * rule available for this particular error) state_stack: (array: the current
+ * parser LALR/LR internal state stack; this can be used, for instance, for
+ * advanced error analysis and reporting) value_stack: (array: the current
+ * parser LALR/LR internal `$$` value stack; this can be used, for instance, for
+ * advanced error analysis and reporting) location_stack: (array: the current
+ * parser LALR/LR internal location stack; this can be used, for instance, for
+ * advanced error analysis and reporting) yy:          (object: the current
+ * parser internal "shared state" `yy` as is also available in the rule actions;
+ * this can be used, for instance, for advanced error analysis and reporting)
+ *    lexer:       (reference to the current lexer instance used by the parser)
+ *    parser:      (reference to the current parser instance)
+ *  }
+ *
+ * while `this` will reference the current parser instance.
+ *
+ * When `parseError` is invoked by the lexer, `this` will still reference the
+ * related *parser* instance, while these additional `hash` fields will also be
+ * provided:
+ *
+ *  {
+ *    lexer:       (reference to the current lexer instance which reported the
+ * error)
+ *  }
+ *
+ * When `parseError` is invoked by the parser due to a **JavaScript exception**
+ * being fired from either the parser or lexer, `this` will still reference the
+ * related *parser* instance, while these additional `hash` fields will also be
+ * provided:
+ *
+ *  {
+ *    exception:   (reference to the exception thrown)
+ *  }
+ *
+ * Please do note that in the latter situation, the `expected` field will be
+ * omitted as this type of failure is assumed not to be due to *parse errors*
+ * but rather due to user action code in either parser or lexer failing
+ * unexpectedly.
+ *
+ * ---
+ *
+ * You can specify parser options by setting / modifying the `.yy` object of
+ * your Parser instance. These options are available:
+ *
+ * ### options which are global for all parser instances
+ *
+ *  Parser.pre_parse: function(yy)
+ *                 optional: you can specify a pre_parse() function in the chunk
+ * following the grammar, i.e. after the last `%%`. Parser.post_parse:
+ * function(yy, retval, parseInfo) { return retval; } optional: you can specify
+ * a post_parse() function in the chunk following the grammar, i.e. after the
+ * last `%%`. When it does not return any value, the parser will return the
+ * original `retval`.
+ *
+ * ### options which can be set up per parser instance
+ *
+ *  yy: {
+ *      pre_parse:  function(yy)
+ *                 optional: is invoked before the parse cycle starts (and
+ * before the first invocation of `lex()`) but immediately after the invocation
+ * of `parser.pre_parse()`). post_parse: function(yy, retval, parseInfo) {
+ * return retval; } optional: is invoked when the parse terminates due to
+ * success ('accept') or failure (even when exceptions are thrown). `retval`
+ * contains the return value to be produced by `Parser.parse()`; this function
+ * can override the return value by returning another. When it does not return
+ * any value, the parser will return the original `retval`. This function is
+ * invoked immediately before `parser.post_parse()`.
+ *
+ *      parseError: function(str, hash, ExceptionClass)
+ *                 optional: overrides the default `parseError` function.
+ *      quoteName: function(name),
+ *                 optional: overrides the default `quoteName` function.
+ *  }
+ *
+ *  parser.lexer.options: {
+ *      pre_lex:  function()
+ *                 optional: is invoked before the lexer is invoked to produce
+ * another token. `this` refers to the Lexer object. post_lex: function(token) {
+ * return token; } optional: is invoked when the lexer has produced a token
+ * `token`; this function can override the returned token value by returning
+ * another. When it does not return any (truthy) value, the lexer will return
+ *                 the original `token`.
+ *                 `this` refers to the Lexer object.
+ *
+ *      ranges: boolean
+ *                 optional: `true` ==> token location info will include a
+ * .range[] member. flex: boolean optional: `true` ==> flex-like lexing
+ * behaviour where the rules are tested exhaustively to find the longest match.
+ *      backtrack_lexer: boolean
+ *                 optional: `true` ==> lexer regexes are tested in order and
+ * for invoked; the lexer terminates the scan when a token is returned by the
+ * action code. xregexp: boolean optional: `true` ==> lexer rule regexes are
+ * "extended regex format" requiring the `XRegExp` library. When this `%option`
+ * has not been specified at compile time, all lexer rule regexes have been
+ * written as standard JavaScript RegExp expressions.
+ *  }
+ */
+
+
+
+var parser = (function() {
+  // See also:
+  // http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508
+  // but we keep the prototype.constructor and prototype.name assignment lines
+  // too for compatibility with userland code which might access the derived
+  // class in a 'classic' way.
+  function JisonParserError(msg, hash) {
+    Object.defineProperty(
+        this, 'name',
+        {enumerable: false, writable: false, value: 'JisonParserError'});
+
+    if (msg == null) {
+      msg = '???';
+    }
+
+    Object.defineProperty(
+        this, 'message', {enumerable: false, writable: true, value: msg});
+
+    this.hash = hash;
+
+    var stacktrace;
+    if (hash && hash.exception instanceof Error) {
+      var ex2 = hash.exception;
+      this.message = ex2.message || msg;
+      stacktrace = ex2.stack;
+    }
+    if (!stacktrace) {
+      if (Error.hasOwnProperty('captureStackTrace')) {  // V8/Chrome engine
+        Error.captureStackTrace(this, this.constructor);
+      } else {
+        stacktrace = (new Error(msg)).stack;
+      }
+    }
+    if (stacktrace) {
+      Object.defineProperty(
+          this, 'stack',
+          {enumerable: false, writable: false, value: stacktrace});
+    }
+  }
+
+  if (typeof Object.setPrototypeOf === 'function') {
+    Object.setPrototypeOf(JisonParserError.prototype, Error.prototype);
+  } else {
+    JisonParserError.prototype = Object.create(Error.prototype);
+  }
+  JisonParserError.prototype.constructor = JisonParserError;
+  JisonParserError.prototype.name = 'JisonParserError';
+
+
+
+  // helper: reconstruct the productions[] table
+  function bp(s) {
+    var rv = [];
+    var p = s.pop;
+    var r = s.rule;
+    for (var i = 0, l = p.length; i < l; i++) {
+      rv.push([p[i], r[i]]);
+    }
+    return rv;
+  }
+
+
+
+  // helper: reconstruct the defaultActions[] table
+  function bda(s) {
+    var rv = {};
+    var d = s.idx;
+    var g = s.goto;
+    for (var i = 0, l = d.length; i < l; i++) {
+      var j = d[i];
+      rv[j] = g[i];
+    }
+    return rv;
+  }
+
+
+
+  // helper: reconstruct the 'goto' table
+  function bt(s) {
+    var rv = [];
+    var d = s.len;
+    var y = s.symbol;
+    var t = s.type;
+    var a = s.state;
+    var m = s.mode;
+    var g = s.goto;
+    for (var i = 0, l = d.length; i < l; i++) {
+      var n = d[i];
+      var q = {};
+      for (var j = 0; j < n; j++) {
+        var z = y.shift();
+        switch (t.shift()) {
+          case 2:
+            q[z] = [m.shift(), g.shift()];
+            break;
+
+          case 0:
+            q[z] = a.shift();
+            break;
+
+          default:
+            // type === 1: accept
+            q[z] = [3];
+        }
+      }
+      rv.push(q);
+    }
+    return rv;
+  }
+
+
+
+  // helper: runlength encoding with increment step: code, length: step (default
+  // step = 0) `this` references an array
+  function s(c, l, a) {
+    a = a || 0;
+    for (var i = 0; i < l; i++) {
+      this.push(c);
+      c += a;
+    }
+  }
+
+  // helper: duplicate sequence from *relative* offset and length.
+  // `this` references an array
+  function c(i, l) {
+    i = this.length - i;
+    for (l += i; i < l; i++) {
+      this.push(this[i]);
+    }
+  }
+
+  // helper: unpack an array using helpers and data, all passed in an array
+  // argument 'a'.
+  function u(a) {
+    var rv = [];
+    for (var i = 0, l = a.length; i < l; i++) {
+      var e = a[i];
+      // Is this entry a helper function?
+      if (typeof e === 'function') {
+        i++;
+        e.apply(rv, a[i]);
+      } else {
+        rv.push(e);
+      }
+    }
+    return rv;
+  }
+
+
+  var parser = {
+    // Code Generator Information Report
+    // ---------------------------------
+    //
+    // Options:
+    //
+    //   default action mode: ............. ["classic","merge"]
+    //   test-compile action mode: ........ "parser:*,lexer:*"
+    //   try..catch: ...................... true
+    //   default resolve on conflict: ..... true
+    //   on-demand look-ahead: ............ false
+    //   error recovery token skip maximum: 3
+    //   yyerror in parse actions is: ..... NOT recoverable,
+    //   yyerror in lexer actions and other non-fatal lexer are:
+    //   .................................. NOT recoverable,
+    //   debug grammar/output: ............ false
+    //   has partial LR conflict upgrade:   true
+    //   rudimentary token-stack support:   false
+    //   parser table compression mode: ... 2
+    //   export debug tables: ............. false
+    //   export *all* tables: ............. false
+    //   module type: ..................... commonjs
+    //   parser engine type: .............. lalr
+    //   output main() in the module: ..... true
+    //   has user-specified main(): ....... false
+    //   has user-specified require()/import modules for main():
+    //   .................................. false
+    //   number of expected conflicts: .... 0
+    //
+    //
+    // Parser Analysis flags:
+    //
+    //   no significant actions (parser is a language matcher only):
+    //   .................................. false
+    //   uses yyleng: ..................... false
+    //   uses yylineno: ................... false
+    //   uses yytext: ..................... false
+    //   uses yylloc: ..................... false
+    //   uses ParseError API: ............. false
+    //   uses YYERROR: .................... false
+    //   uses YYRECOVERING: ............... false
+    //   uses YYERROK: .................... false
+    //   uses YYCLEARIN: .................. false
+    //   tracks rule values: .............. true
+    //   assigns rule values: ............. true
+    //   uses location tracking: .......... false
+    //   assigns location: ................ false
+    //   uses yystack: .................... false
+    //   uses yysstack: ................... false
+    //   uses yysp: ....................... true
+    //   uses yyrulelength: ............... false
+    //   uses yyMergeLocationInfo API: .... false
+    //   has error recovery: .............. false
+    //   has error reporting: ............. false
+    //
+    // --------- END OF REPORT -----------
+
+    trace: function no_op_trace() {},
+    JisonParserError,
+    yy: {},
+    options: {
+      type: 'lalr',
+      hasPartialLrUpgradeOnConflict: true,
+      errorRecoveryTokenDiscardCount: 3
+    },
+    symbols_: {
+      '$accept': 0,
+      '$end': 1,
+      'CHROME_URL': 9,
+      'COMMA': 13,
+      'EOF': 1,
+      'EOL': 4,
+      'EXPECT': 8,
+      'FOCUS': 15,
+      'HTML_SNIPPET': 10,
+      'LEFT_PARENS': 11,
+      'LOAD_PAGE': 3,
+      'NEXT': 5,
+      'NUMBER': 12,
+      'ON': 16,
+      'PREVIOUS': 6,
+      'RIGHT_PARENS': 14,
+      'ROLE': 17,
+      'SELECT': 7,
+      'STRING_LITERAL': 18,
+      'error': 2,
+      'expectation_expression': 26,
+      'focusable_expression': 27,
+      'html_page': 24,
+      'load_expression': 20,
+      'page_expression': 23,
+      'point_expression': 25,
+      'statement': 22,
+      'statements': 21,
+      'string_literal': 28,
+      'test': 19
+    },
+    terminals_: {
+      1: 'EOF',
+      2: 'error',
+      3: 'LOAD_PAGE',
+      4: 'EOL',
+      5: 'NEXT',
+      6: 'PREVIOUS',
+      7: 'SELECT',
+      8: 'EXPECT',
+      9: 'CHROME_URL',
+      10: 'HTML_SNIPPET',
+      11: 'LEFT_PARENS',
+      12: 'NUMBER',
+      13: 'COMMA',
+      14: 'RIGHT_PARENS',
+      15: 'FOCUS',
+      16: 'ON',
+      17: 'ROLE',
+      18: 'STRING_LITERAL'
+    },
+    TERROR: 2,
+    EOF: 1,
+
+    // internals: defined here so the object *structure* doesn't get modified by
+    // parse() et al,
+    // thus helping JIT compilers like Chrome V8.
+    originalQuoteName: null,
+    originalParseError: null,
+    cleanupAfterParse: null,
+    constructParseErrorInfo: null,
+    yyMergeLocationInfo: null,
+
+    __reentrant_call_depth: 0,  // INTERNAL USE ONLY
+    __error_infos: [],  // INTERNAL USE ONLY: the set of parseErrorInfo objects
+                        // created since the last cleanup
+    __error_recovery_infos: [],  // INTERNAL USE ONLY: the set of parseErrorInfo
+                                 // objects created since the last cleanup
+
+    // APIs which will be set up depending on user action code analysis:
+    // yyRecovering: 0,
+    // yyErrOk: 0,
+    // yyClearIn: 0,
+
+    // Helper APIs
+    // -----------
+
+    // Helper function which can be overridden by user code later on: put
+    // suitable quotes around
+    // literal IDs in a description string.
+    quoteName: function parser_quoteName(id_str) {
+      return '"' + id_str + '"';
+    },
+
+    // Return the name of the given symbol (terminal or non-terminal) as a
+    // string, when available.
+    //
+    // Return NULL when the symbol is unknown to the parser.
+    getSymbolName: function parser_getSymbolName(symbol) {
+      if (this.terminals_[symbol]) {
+        return this.terminals_[symbol];
+      }
+
+      // Otherwise... this might refer to a RULE token i.e. a non-terminal: see
+      // if we can dig that one up.
+      //
+      // An example of this may be where a rule's action code contains a call
+      // like this:
+      //
+      //      parser.getSymbolName(#$)
+      //
+      // to obtain a human-readable name of the current grammar rule.
+      var s = this.symbols_;
+      for (var key in s) {
+        if (s[key] === symbol) {
+          return key;
+        }
+      }
+      return null;
+    },
+
+    // Return a more-or-less human-readable description of the given symbol,
+    // when available,
+    // or the symbol itself, serving as its own 'description' for lack of
+    // something better to serve up.
+    //
+    // Return NULL when the symbol is unknown to the parser.
+    describeSymbol: function parser_describeSymbol(symbol) {
+      if (symbol !== this.EOF && this.terminal_descriptions_ &&
+          this.terminal_descriptions_[symbol]) {
+        return this.terminal_descriptions_[symbol];
+      } else if (symbol === this.EOF) {
+        return 'end of input';
+      }
+      var id = this.getSymbolName(symbol);
+      if (id) {
+        return this.quoteName(id);
+      }
+      return null;
+    },
+
+    // Produce a (more or less) human-readable list of expected tokens at the
+    // point of failure.
+    //
+    // The produced list may contain token or token set descriptions instead of
+    // the tokens
+    // themselves to help turning this output into something that easier to read
+    // by humans
+    // unless `do_not_describe` parameter is set, in which case a list of the
+    // raw, *numeric*,
+    // expected terminals and nonterminals is produced.
+    //
+    // The returned list (array) will not contain any duplicate entries.
+    collect_expected_token_set: function parser_collect_expected_token_set(
+        state, do_not_describe) {
+      var TERROR = this.TERROR;
+      var tokenset = [];
+      var check = {};
+      // Has this (error?) state been outfitted with a custom expectations
+      // description text for human consumption? If so, use that one instead of
+      // the less palatable token set.
+      if (!do_not_describe && this.state_descriptions_ &&
+          this.state_descriptions_[state]) {
+        return [this.state_descriptions_[state]];
+      }
+      for (var p in this.table[state]) {
+        p = +p;
+        if (p !== TERROR) {
+          var d = do_not_describe ? p : this.describeSymbol(p);
+          if (d && !check[d]) {
+            tokenset.push(d);
+            check[d] =
+                true;  // Mark this token description as already mentioned to
+                       // prevent outputting duplicate entries.
+          }
+        }
+      }
+      return tokenset;
+    },
+    productions_: bp({
+      pop: u([
+        19, 19, 20, 20, s, [21, 4], s, [22, 4], 23, 23, 24, 24, 25, 25, 26, s,
+        [27, 4], 28
+      ]),
+      rule: u([
+        3, 2, 2, 3, s, [3, 4, -1], 1, 1, 2, 2, c, [4, 3], 1, 5, 0, c, [18, 3],
+        s, [1, 3]
+      ])
+    }),
+    performAction: function parser__PerformAction(
+        yystate /* action[1] */, yysp, yyvstack) {
+      /* this == yyval */
+
+      // the JS engine itself can go and remove these statements when `yy` turns
+      // out to be unused in any action code!
+      var yy = this.yy;
+      var yyparser = yy.parser;
+      var yylexer = yy.lexer;
+
+
+
+      switch (yystate) {
+        case 0:
+          /*! Production::    $accept : test $end */
+
+          // default action (generated by JISON mode classic/merge ::
+          // 1,VT,VA,-,-,-,-,-,-):
+          this.$ = yyvstack[yysp - 1];
+          // END of default action (generated by JISON mode classic/merge ::
+          // 1,VT,VA,-,-,-,-,-,-)
+          break;
+
+        case 1:
+          /*! Production::    test : load_expression statements EOF */
+
+          this.$ = {};
+          if (yyvstack[yysp - 2].page.type === 'HTML') {
+            this.$.output = initTest(yyvstack[yysp - 2].page);
+          } else {
+            this.$.output = initTest();
+          }
+
+          this.$.output += addAllStatements(yyvstack[yysp - 1]);
+
+          if (yyvstack[yysp - 2].page.type === 'ChromeURL') {
+            this.$.output += finishTest(yyvstack[yysp - 2].page);
+          } else {
+            this.$.output += finishTest();
+          }
+
+          yyvstack[yysp - 1].unshift(yyvstack[yysp - 2]);
+          this.$.ast = yyvstack[yysp - 1];
+          return this.$;
+          break;
+
+        case 2:
+          /*! Production::    test : statements EOF */
+
+          this.$ = {ast: yyvstack[yysp - 1]};
+          this.$.output = initTest();
+          this.$.output += addAllStatements(yyvstack[yysp - 1]);
+          this.$.output += finishTest();
+
+          return this.$;
+          break;
+
+        case 3:
+          /*! Production::    load_expression : LOAD_PAGE page_expression */
+        case 4:
+          /*! Production::    load_expression : LOAD_PAGE EOL page_expression */
+
+          this.$ = {command: 'Load', page: yyvstack[yysp]};
+          break;
+
+        case 5:
+          /*! Production::    statements : statements EOL statement */
+
+          this.$ = yyvstack[yysp - 2];
+          this.$.push(yyvstack[yysp]);
+          break;
+
+        case 6:
+          /*! Production::    statements : statements EOL */
+
+          this.$ = yyvstack[yysp - 1];
+          break;
+
+        case 7:
+          /*! Production::    statements : statement */
+
+          this.$ = [yyvstack[yysp]];
+          break;
+
+        case 8:
+          /*! Production::    statements : %epsilon */
+
+          this.$ = [];
+          break;
+
+        case 9:
+          /*! Production::    statement : NEXT */
+
+          this.$ = {command: 'Next'};
+          this.$.output = 'TestUtility.pressNextSwitch();';
+          break;
+
+        case 10:
+          /*! Production::    statement : PREVIOUS */
+
+          this.$ = {command: 'Previous'};
+          this.$.output = 'TestUtility.pressPreviousSwitch();';
+          break;
+
+        case 11:
+          /*! Production::    statement : SELECT point_expression */
+
+          this.$ = {command: 'Select'};
+          // A point_expression is only expected when point_scan is enabled.
+          if (yyvstack[yysp]) {
+            this.$.point = yyvstack[yysp];
+            point = this.$.point.output;
+            this.$.output = `TestUtility.simulatePointScanSelect(${point});`;
+          } else {
+            this.$.output = 'TestUtility.pressSelectSwitch();';
+          }
+          break;
+
+        case 12:
+          /*! Production::    statement : EXPECT expectation_expression */
+
+          this.$ = {command: 'Expect', expectation: yyvstack[yysp]};
+          this.$.output = yyvstack[yysp].output;
+          break;
+
+        case 13:
+          /*! Production::    page_expression : html_page */
+
+          this.$ = {type: 'HTML', value: yyvstack[yysp]};
+          page = this.$.value;
+          this.$.output = `'` + page + `'`;
+          break;
+
+        case 14:
+          /*! Production::    page_expression : CHROME_URL */
+
+          this.$ = {type: 'ChromeURL', value: yyvstack[yysp]};
+          page = this.$.value;
+          this.$.output = `'` + page + `'`;
+          break;
+
+        case 15:
+          /*! Production::    html_page : html_page HTML_SNIPPET */
+
+          this.$ = yyvstack[yysp - 1] + '\n' + yyvstack[yysp];
+          break;
+
+        case 16:
+          /*! Production::    html_page : HTML_SNIPPET */
+
+          this.$ = yyvstack[yysp];
+          break;
+
+        case 17:
+          /*! Production::    point_expression : LEFT_PARENS NUMBER COMMA NUMBER
+           * RIGHT_PARENS */
+
+          this.$ = {x: yyvstack[yysp - 3], y: yyvstack[yysp - 1]};
+          point = this.$;
+          this.$.output = `{x: ${point.x}, y: ${point.y}}`;
+          break;
+
+        case 18:
+          /*! Production::    point_expression : %epsilon */
+
+          this.$ = null;
+          break;
+
+        case 19:
+          /*! Production::    expectation_expression : FOCUS ON
+           * focusable_expression */
+
+          this.$ = {type: 'Focus', matches: yyvstack[yysp]};
+          objectToMatch = this.$.matches.output;
+          this.$.output = `await TestUtility.expectFocusOn(${objectToMatch});`;
+          break;
+
+        case 20:
+          /*! Production::    focusable_expression : ROLE string_literal */
+
+          this.$ = {role: yyvstack[yysp - 1], name: yyvstack[yysp]};
+          objectToMatch = this.$;
+          this.$.output =
+              `{role: '${objectToMatch.role}', name: '${objectToMatch.name}'}`;
+          break;
+
+        case 21:
+          /*! Production::    focusable_expression : string_literal ROLE */
+
+          this.$ = {role: yyvstack[yysp], name: yyvstack[yysp - 1]};
+          objectToMatch = this.$;
+          this.$.output =
+              `{role: '${objectToMatch.role}', name: '${objectToMatch.name}'}`;
+          break;
+
+        case 22:
+          /*! Production::    focusable_expression : ROLE */
+
+          this.$ = {role: yyvstack[yysp]};
+          objectToMatch = this.$;
+          this.$.output = `{role: '${objectToMatch.role}'}`;
+          break;
+
+        case 23:
+          /*! Production::    focusable_expression : string_literal */
+
+          this.$ = {name: yyvstack[yysp]};
+          objectToMatch = this.$;
+          this.$.output = `{name: '${objectToMatch.name}'}`;
+          break;
+
+        case 24:
+          /*! Production::    string_literal : STRING_LITERAL */
+
+          // Remove the quotes from the string.
+          this.$ = yyvstack[yysp].substring(1, yyvstack[yysp].length - 1);
+          break;
+      }
+    },
+    table: bt({
+      len: u([
+        11, 1,      8, 2, 5, s, [0, 3], 4, 2, 2, 0, 7, 0, 4, 7,
+        s,  [0, 3], 1, 0, 1, s, [0, 4], 1, 4, 1, 0, 4, 3, c, [12, 5]
+      ]),
+      symbol: u([
+        1,  s,      [3, 6, 1], s,  [19, 4, 1], 1,  1,  c,  [11, 5],
+        c,  [9, 3], 4,         4,  9,          10, 23, 24, 1,
+        4,  11,     25,        15, 26,         1,  4,  c,  [23, 6],
+        22, c,      [19, 6],   c,  [11, 4],    10, 12, 16, 13,
+        17, 18,     27,        28, 12,         1,  4,  18, 28,
+        1,  4,      17,        14
+      ]),
+      type: u([
+        s,       [2, 7], s,       [0, 4], 1,       c, [11, 8], c,
+        [7, 10], 0,      2,       c,      [13, 6], c, [11, 5], c,
+        [26, 8], c,      [51, 9], c,      [23, 7], 2, 2
+      ]),
+      state: u([1, 2, 3, 5, 10, 5, 13, 15, 18, 20, 23, 24, 15, 29, 31, 34]),
+      mode: u([
+        2, 1,       2, s,       [1, 4], 2,      c, [6, 5],   s, [1, 5],
+        c, [11, 6], c, [17, 8], s,      [2, 6], c, [27, 10], c, [12, 4]
+      ]),
+      goto: u([
+        8,      4,  8,       s,  [6, 4, 1], 8,  c,       [6, 5], 11,         12,
+        14,     16, 17,      18, 18,        19, 21,      22,     12,         s,
+        [6, 3], c,  [17, 3], 16, 17,        s,  [13, 6], s,      [25, 4, 1], 30,
+        32,     33, 22,      22, 32,        23, 23,      35,     36
+      ])
+    }),
+    defaultActions: bda({
+      idx: u(
+          [5, 6, 7, 11, 13, 16, 17, 18, 20, s, [22, 4, 1], 29, 32, 34, 35, 36]),
+      goto: u([7, 9, 10, 2, 3, 14, 16, 11, 12, 1, 5, 4, 15, 19, 24, 20, 21, 17])
+    }),
+    parseError: function parseError(str, hash, ExceptionClass) {
+      if (hash.recoverable) {
+        if (typeof this.trace === 'function') {
+          this.trace(str);
+        }
+        hash.destroy();  // destroy... well, *almost*!
+      } else {
+        if (typeof this.trace === 'function') {
+          this.trace(str);
+        }
+        if (!ExceptionClass) {
+          ExceptionClass = this.JisonParserError;
+        }
+        throw new ExceptionClass(str, hash);
+      }
+    },
+    parse: function parse(input) {
+      var self = this;
+      var stack = new Array(128);  // token stack: stores token which leads to
+                                   // state at the same index (column storage)
+      var sstack =
+          new Array(128);  // state stack: stores states (column storage)
+
+      var vstack = new Array(128);  // semantic value stack
+
+      var table = this.table;
+      var sp = 0;  // 'stack pointer': index into the stacks
+
+
+
+      var symbol = 0;
+
+
+
+      var TERROR = this.TERROR;
+      var EOF = this.EOF;
+      var ERROR_RECOVERY_TOKEN_DISCARD_COUNT =
+          (this.options.errorRecoveryTokenDiscardCount | 0) || 3;
+      var NO_ACTION = [
+        0, 37 /* === table.length :: ensures that anyone using this new state
+                 will fail dramatically! */
+      ];
+
+      var lexer;
+      if (this.__lexer__) {
+        lexer = this.__lexer__;
+      } else {
+        lexer = this.__lexer__ = Object.create(this.lexer);
+      }
+
+      var sharedStateYy = {
+        parseError: undefined,
+        quoteName: undefined,
+        lexer: undefined,
+        parser: undefined,
+        pre_parse: undefined,
+        post_parse: undefined,
+        pre_lex: undefined,
+        post_lex:
+            undefined  // WARNING: must be written this way for the code
+                       // expanders to work correctly in both ES5 and ES6 modes!
+      };
+
+      var ASSERT;
+      if (typeof assert !== 'function') {
+        ASSERT = function JisonAssert(cond, msg) {
+          if (!cond) {
+            throw new Error('assertion failed: ' + (msg || '***'));
+          }
+        };
+      } else {
+        ASSERT = assert;
+      }
+
+      this.yyGetSharedState = function yyGetSharedState() {
+        return sharedStateYy;
+      };
+
+
+
+      function shallow_copy_noclobber(dst, src) {
+        for (var k in src) {
+          if (typeof dst[k] === 'undefined' &&
+              Object.prototype.hasOwnProperty.call(src, k)) {
+            dst[k] = src[k];
+          }
+        }
+      }
+
+      // copy state
+      shallow_copy_noclobber(sharedStateYy, this.yy);
+
+      sharedStateYy.lexer = lexer;
+      sharedStateYy.parser = this;
+
+
+
+      // Does the shared state override the default `parseError` that already
+      // comes with this instance?
+      if (typeof sharedStateYy.parseError === 'function') {
+        this.parseError = function parseErrorAlt(str, hash, ExceptionClass) {
+          if (!ExceptionClass) {
+            ExceptionClass = this.JisonParserError;
+          }
+          return sharedStateYy.parseError.call(this, str, hash, ExceptionClass);
+        };
+      } else {
+        this.parseError = this.originalParseError;
+      }
+
+      // Does the shared state override the default `quoteName` that already
+      // comes with this instance?
+      if (typeof sharedStateYy.quoteName === 'function') {
+        this.quoteName = function quoteNameAlt(id_str) {
+          return sharedStateYy.quoteName.call(this, id_str);
+        };
+      } else {
+        this.quoteName = this.originalQuoteName;
+      }
+
+      // set up the cleanup function; make it an API so that external code can
+      // re-use this one in case of calamities or when the `%options
+      // no-try-catch` option has been specified for the grammar, in which case
+      // this parse() API method doesn't come with a `finally { ... }` block any
+      // more!
+      //
+      // NOTE: as this API uses parse() as a closure, it MUST be set again on
+      // every parse() invocation,
+      //       or else your `sharedState`, etc. references will be *wrong*!
+      this.cleanupAfterParse = function parser_cleanupAfterParse(
+          resultValue, invoke_post_methods, do_not_nuke_errorinfos) {
+        var rv;
+
+        if (invoke_post_methods) {
+          var hash;
+
+          if (sharedStateYy.post_parse || this.post_parse) {
+            // create an error hash info instance: we re-use this API in a
+            // **non-error situation** as this one delivers all parser internals
+            // ready for access by userland code.
+            hash = this.constructParseErrorInfo(
+                null /* no error! */, null /* no exception! */, null, false);
+          }
+
+          if (sharedStateYy.post_parse) {
+            rv = sharedStateYy.post_parse.call(
+                this, sharedStateYy, resultValue, hash);
+            if (typeof rv !== 'undefined') {
+              resultValue = rv;
+            }
+          }
+          if (this.post_parse) {
+            rv = this.post_parse.call(this, sharedStateYy, resultValue, hash);
+            if (typeof rv !== 'undefined') {
+              resultValue = rv;
+            }
+          }
+
+          // cleanup:
+          if (hash && hash.destroy) {
+            hash.destroy();
+          }
+        }
+
+        if (this.__reentrant_call_depth > 1) {
+          return resultValue;
+        }  // do not (yet) kill the sharedState when this is
+           // a reentrant run.
+
+        // clean up the lingering lexer structures as well:
+        if (lexer.cleanupAfterLex) {
+          lexer.cleanupAfterLex(do_not_nuke_errorinfos);
+        }
+
+        // prevent lingering circular references from causing memory leaks:
+        if (sharedStateYy) {
+          sharedStateYy.lexer = undefined;
+          sharedStateYy.parser = undefined;
+          if (lexer.yy === sharedStateYy) {
+            lexer.yy = undefined;
+          }
+        }
+        sharedStateYy = undefined;
+        this.parseError = this.originalParseError;
+        this.quoteName = this.originalQuoteName;
+
+        // nuke the vstack[] array at least as that one will still reference
+        // obsoleted user values. To be safe, we nuke the other internal stack
+        // columns as well...
+        stack.length =
+            0;  // fastest way to nuke an array without overly bothering the GC
+        sstack.length = 0;
+
+        vstack.length = 0;
+        sp = 0;
+
+        // nuke the error hash info instances created during this run.
+        // Userland code must COPY any data/references
+        // in the error hash instance(s) it is more permanently interested in.
+        if (!do_not_nuke_errorinfos) {
+          for (var i = this.__error_infos.length - 1; i >= 0; i--) {
+            var el = this.__error_infos[i];
+            if (el && typeof el.destroy === 'function') {
+              el.destroy();
+            }
+          }
+          this.__error_infos.length = 0;
+        }
+
+        return resultValue;
+      };
+
+
+
+      // NOTE: as this API uses parse() as a closure, it MUST be set again on
+      // every parse() invocation,
+      //       or else your `lexer`, `sharedState`, etc. references will be
+      //       *wrong*!
+      this.constructParseErrorInfo = function parser_constructParseErrorInfo(
+          msg, ex, expected, recoverable) {
+        var pei = {
+          errStr: msg,
+          exception: ex,
+          text: lexer.match,
+          value: lexer.yytext,
+          token: this.describeSymbol(symbol) || symbol,
+          token_id: symbol,
+          line: lexer.yylineno,
+
+          expected,
+          recoverable,
+          state,
+          action,
+          new_state: newState,
+          symbol_stack: stack,
+          state_stack: sstack,
+          value_stack: vstack,
+
+          stack_pointer: sp,
+          yy: sharedStateYy,
+          lexer,
+          parser: this,
+
+          // and make sure the error info doesn't stay due to potential
+          // ref cycle via userland code manipulations.
+          // These would otherwise all be memory leak opportunities!
+          //
+          // Note that only array and object references are nuked as those
+          // constitute the set of elements which can produce a cyclic ref.
+          // The rest of the members is kept intact as they are harmless.
+          destroy: function destructParseErrorInfo() {
+            // remove cyclic references added to error info:
+            // info.yy = null;
+            // info.lexer = null;
+            // info.value = null;
+            // info.value_stack = null;
+            // ...
+            var rec = !!this.recoverable;
+            for (var key in this) {
+              if (this.hasOwnProperty(key) && typeof key === 'object') {
+                this[key] = undefined;
+              }
+            }
+            this.recoverable = rec;
+          }
+        };
+        // track this instance so we can `destroy()` it once we deem it
+        // superfluous and ready for garbage collection!
+        this.__error_infos.push(pei);
+        return pei;
+      };
+
+
+
+      function getNonTerminalFromCode(symbol) {
+        var tokenName = self.getSymbolName(symbol);
+        if (!tokenName) {
+          tokenName = symbol;
+        }
+        return tokenName;
+      }
+
+
+      function stdLex() {
+        var token = lexer.lex();
+        // if token isn't its numeric value, convert
+        if (typeof token !== 'number') {
+          token = self.symbols_[token] || token;
+        }
+
+        return token || EOF;
+      }
+
+      function fastLex() {
+        var token = lexer.fastLex();
+        // if token isn't its numeric value, convert
+        if (typeof token !== 'number') {
+          token = self.symbols_[token] || token;
+        }
+
+        return token || EOF;
+      }
+
+      var lex = stdLex;
+
+
+      var state, action, r, t;
+      var yyval = {$: true, _$: undefined, yy: sharedStateYy};
+      var p;
+      var yyrulelen;
+      var thisProduction;
+      var newState;
+      var retval = false;
+
+
+      try {
+        this.__reentrant_call_depth++;
+
+        lexer.setInput(input, sharedStateYy);
+
+        // NOTE: we *assume* no lexer pre/post handlers are set up *after*
+        // this initial `setInput()` call: hence we can now check and decide
+        // whether we'll go with the standard, slower, lex() API or the
+        // `fast_lex()` one:
+        if (typeof lexer.canIUse === 'function') {
+          var lexerInfo = lexer.canIUse();
+          if (lexerInfo.fastLex && typeof fastLex === 'function') {
+            lex = fastLex;
+          }
+        }
+
+
+
+        vstack[sp] = null;
+        sstack[sp] = 0;
+        stack[sp] = 0;
+        ++sp;
+
+
+
+        if (this.pre_parse) {
+          this.pre_parse.call(this, sharedStateYy);
+        }
+        if (sharedStateYy.pre_parse) {
+          sharedStateYy.pre_parse.call(this, sharedStateYy);
+        }
+
+        newState = sstack[sp - 1];
+        for (;;) {
+          // retrieve state number from top of stack
+          state = newState;  // sstack[sp - 1];
+
+          // use default actions if available
+          if (this.defaultActions[state]) {
+            action = 2;
+            newState = this.defaultActions[state];
+          } else {
+            // The single `==` condition below covers both these `===`
+            // comparisons in a single operation:
+            //
+            //     if (symbol === null || typeof symbol === 'undefined') ...
+            if (!symbol) {
+              symbol = lex();
+            }
+            // read action for current state and first input
+            t = (table[state] && table[state][symbol]) || NO_ACTION;
+            newState = t[1];
+            action = t[0];
+
+
+
+            // handle parse error
+            if (!action) {
+              var errStr;
+              var errSymbolDescr = (this.describeSymbol(symbol) || symbol);
+              var expected = this.collect_expected_token_set(state);
+
+              // Report error
+              if (typeof lexer.yylineno === 'number') {
+                errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': ';
+              } else {
+                errStr = 'Parse error: ';
+              }
+              if (typeof lexer.showPosition === 'function') {
+                errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n';
+              }
+              if (expected.length) {
+                errStr += 'Expecting ' + expected.join(', ') +
+                    ', got unexpected ' + errSymbolDescr;
+              } else {
+                errStr += 'Unexpected ' + errSymbolDescr;
+              }
+              // we cannot recover from the error!
+              p = this.constructParseErrorInfo(errStr, null, expected, false);
+              r = this.parseError(p.errStr, p, this.JisonParserError);
+              if (typeof r !== 'undefined') {
+                retval = r;
+              }
+              break;
+            }
+          }
+
+
+
+          switch (action) {
+            // catch misc. parse failures:
+            default:
+              // this shouldn't happen, unless resolve defaults are off
+              if (action instanceof Array) {
+                p = this.constructParseErrorInfo(
+                    'Parse Error: multiple actions possible at state: ' +
+                        state + ', token: ' + symbol,
+                    null, null, false);
+                r = this.parseError(p.errStr, p, this.JisonParserError);
+                if (typeof r !== 'undefined') {
+                  retval = r;
+                }
+                break;
+              }
+              // Another case of better safe than sorry: in case state
+              // transitions come out of another error recovery process or a
+              // buggy LUT (LookUp Table):
+              p = this.constructParseErrorInfo(
+                  'Parsing halted. No viable error recovery approach ' +
+                      'available due to internal system failure.',
+                  null, null, false);
+              r = this.parseError(p.errStr, p, this.JisonParserError);
+              if (typeof r !== 'undefined') {
+                retval = r;
+              }
+              break;
+
+            // shift:
+            case 1:
+              stack[sp] = symbol;
+              vstack[sp] = lexer.yytext;
+
+              sstack[sp] = newState;  // push state
+
+              ++sp;
+              symbol = 0;
+
+
+
+              // Pick up the lexer details for the current symbol as that one is
+              // not 'look-ahead' any more:
+
+
+
+              continue;
+
+            // reduce:
+            case 2:
+
+
+
+              thisProduction =
+                  this.productions_[newState - 1];  // `this.productions_[]` is
+                                                    // zero-based indexed while
+                                                    // states start from 1
+                                                    // upwards...
+              yyrulelen = thisProduction[1];
+
+
+
+              r = this.performAction.call(yyval, newState, sp - 1, vstack);
+
+              if (typeof r !== 'undefined') {
+                retval = r;
+                break;
+              }
+
+              // pop off stack
+              sp -= yyrulelen;
+
+              // don't overwrite the `symbol` variable: use a local var to speed
+              // things up:
+              var ntsymbol = thisProduction[0];  // push nonterminal (reduce)
+              stack[sp] = ntsymbol;
+              vstack[sp] = yyval.$;
+
+              // goto new state = table[STATE][NONTERMINAL]
+              newState = table[sstack[sp - 1]][ntsymbol];
+              sstack[sp] = newState;
+              ++sp;
+
+
+
+              continue;
+
+            // accept:
+            case 3:
+              if (sp !== -2) {
+                retval = true;
+                // Return the `$accept` rule's `$$` result, if available.
+                //
+                // Also note that JISON always adds this top-most `$accept` rule
+                // (with implicit, default, action):
+                //
+                //     $accept: <startSymbol> $end
+                //                  %{ $$ = $1; @$ = @1; %}
+                //
+                // which, combined with the parse kernel's `$accept` state
+                // behaviour coded below, will produce the `$$` value output of
+                // the <startSymbol> rule as the parse result, IFF that result
+                // is *not* `undefined`. (See also the parser kernel code.)
+                //
+                // In code:
+                //
+                //                  %{
+                //                      @$ = @1;            // if location
+                //                      tracking support is included if (typeof
+                //                      $1 !== 'undefined')
+                //                          return $1;
+                //                      else
+                //                          return true;           // the
+                //                          default parse result if the rule
+                //                          actions don't produce anything
+                //                  %}
+                sp--;
+                if (typeof vstack[sp] !== 'undefined') {
+                  retval = vstack[sp];
+                }
+              }
+              break;
+          }
+
+          // break out of loop: we accept or fail with error
+          break;
+        }
+      } catch (ex) {
+        // report exceptions through the parseError callback too, but keep the
+        // exception intact if it is a known parser or lexer error which has
+        // been thrown by parseError() already:
+        if (ex instanceof this.JisonParserError) {
+          throw ex;
+        } else if (
+            lexer && typeof lexer.JisonLexerError === 'function' &&
+            ex instanceof lexer.JisonLexerError) {
+          throw ex;
+        }
+
+        p = this.constructParseErrorInfo(
+            'Parsing aborted due to exception.', ex, null, false);
+        retval = false;
+        r = this.parseError(p.errStr, p, this.JisonParserError);
+        if (typeof r !== 'undefined') {
+          retval = r;
+        }
+      } finally {
+        retval = this.cleanupAfterParse(retval, true, true);
+        this.__reentrant_call_depth--;
+      }  // /finally
+
+      return retval;
+    }
+  };
+  parser.originalParseError = parser.parseError;
+  parser.originalQuoteName = parser.quoteName;
+  /* lexer generated by jison-lex 0.6.1-215 */
+
+  /*
+   * Returns a Lexer object of the following structure:
+   *
+   *  Lexer: {
+   *    yy: {}     The so-called "shared state" or rather the *source* of it;
+   *               the real "shared state" `yy` passed around to
+   *               the rule actions, etc. is a direct reference!
+   *
+   *               This "shared context" object was passed to the lexer by way
+   * of the `lexer.setInput(str, yy)` API before you may use it.
+   *
+   *               This "shared context" object is passed to the lexer action
+   * code in `performAction()` so userland code in the lexer actions may
+   * communicate with the outside world and/or other lexer rules' actions in
+   * more or less complex ways.
+   *
+   *  }
+   *
+   *  Lexer.prototype: {
+   *    EOF: 1,
+   *    ERROR: 2,
+   *
+   *    yy:        The overall "shared context" object reference.
+   *
+   *    JisonLexerError: function(msg, hash),
+   *
+   *    performAction: function lexer__performAction(yy, yyrulenumber,
+   * YY_START),
+   *
+   *               The function parameters and `this` have the following
+   * value/meaning:
+   *               - `this`    : reference to the `lexer` instance.
+   *                               `yY` is an alias for `this` lexer instance
+   * reference used internally.
+   *
+   *               - `yy`      : a reference to the `yy` "shared state" object
+   * which was passed to the lexer by way of the `lexer.setInput(str, yy)` API
+   * before.
+   *
+   *                             Note:
+   *                             The extra arguments you specified in the
+   * `%parse-param` statement in your
+   *                             **parser** grammar definition file are passed
+   * to the lexer via this object reference as member variables.
+   *
+   *               - `yyrulenumber`   : index of the matched lexer rule (regex),
+   * used internally.
+   *
+   *               - `YY_START`: the current lexer "start condition" state.
+   *
+   *    parseError: function(str, hash, ExceptionClass),
+   *
+   *    constructLexErrorInfo: function(error_message, is_recoverable),
+   *               Helper function.
+   *               Produces a new errorInfo 'hash object' which can be passed
+   * into `parseError()`. See it's use in this lexer kernel in many places;
+   * example usage:
+   *
+   *                   var infoObj = lexer.constructParseErrorInfo('fail!',
+   * true); var retVal = lexer.parseError(infoObj.errStr, infoObj,
+   * lexer.JisonLexerError);
+   *
+   *    options: { ... lexer %options ... },
+   *
+   *    lex: function(),
+   *               Produce one token of lexed input, which was passed in earlier
+   * via the `lexer.setInput()` API. You MAY use the additional `args...`
+   * parameters as per `%parse-param` spec of the **lexer** grammar: these extra
+   * `args...` are added verbatim to the `yy` object reference as member
+   * variables.
+   *
+   *               WARNING:
+   *               Lexer's additional `args...` parameters (via lexer's
+   * `%parse-param`) MAY conflict with any attributes already added to `yy` by
+   * the **parser** or the jison run-time; when such a collision is detected an
+   * exception is thrown to prevent the generated run-time from silently
+   * accepting this confusing and potentially hazardous situation!
+   *
+   *    cleanupAfterLex: function(do_not_nuke_errorinfos),
+   *               Helper function.
+   *
+   *               This helper API is invoked when the **parse process** has
+   * completed: it is the responsibility of the **parser** (or the calling
+   * userland code) to invoke this method once cleanup is desired.
+   *
+   *               This helper may be invoked by user code to ensure the
+   * internal lexer gets properly garbage collected.
+   *
+   *    setInput: function(input, [yy]),
+   *
+   *
+   *    input: function(),
+   *
+   *
+   *    unput: function(str),
+   *
+   *
+   *    more: function(),
+   *
+   *
+   *    reject: function(),
+   *
+   *
+   *    less: function(n),
+   *
+   *
+   *    pastInput: function(n),
+   *
+   *
+   *    upcomingInput: function(n),
+   *
+   *
+   *    showPosition: function(),
+   *
+   *
+   *    test_match: function(regex_match_array, rule_index),
+   *
+   *
+   *    next: function(),
+   *
+   *
+   *    begin: function(condition),
+   *
+   *
+   *    pushState: function(condition),
+   *
+   *
+   *    popState: function(),
+   *
+   *
+   *    topState: function(),
+   *
+   *
+   *    _currentRules: function(),
+   *
+   *
+   *    stateStackSize: function(),
+   *
+   *
+   *    performAction: function(yy, yY, yyrulenumber, YY_START),
+   *
+   *
+   *    rules: [...],
+   *
+   *
+   *    conditions: {associative list: name ==> set},
+   *  }
+   *
+   *
+   *  token location info (`yylloc`): {
+   *    first_line: n,
+   *    last_line: n,
+   *    first_column: n,
+   *    last_column: n,
+   *    range: [start_number, end_number]
+   *               (where the numbers are indexes into the input string,
+   * zero-based)
+   *  }
+   *
+   * ---
+   *
+   * The `parseError` function receives a 'hash' object with these members for
+   * lexer errors:
+   *
+   *  {
+   *    text:        (matched text)
+   *    token:       (the produced terminal token, if any)
+   *    token_id:    (the produced terminal token numeric ID, if any)
+   *    line:        (yylineno)
+   *    loc:         (yylloc)
+   *    recoverable: (boolean: TRUE when the parser MAY have an error recovery
+   * rule available for this particular error) yy:          (object: the current
+   * parser internal "shared state" `yy` as is also available in the rule
+   * actions; this can be used, for instance, for advanced error analysis and
+   * reporting) lexer:       (reference to the current lexer instance used by
+   * the parser)
+   *  }
+   *
+   * while `this` will reference the current lexer instance.
+   *
+   * When `parseError` is invoked by the lexer, the default implementation will
+   * attempt to invoke `yy.parser.parseError()`; when this callback is not
+   * provided it will try to invoke `yy.parseError()` instead. When that
+   * callback is also not provided, a `JisonLexerError` exception will be thrown
+   * containing the error message and `hash`, as constructed by the
+   * `constructLexErrorInfo()` API.
+   *
+   * Note that the lexer's `JisonLexerError` error class is passed via the
+   * `ExceptionClass` argument, which is invoked to construct the exception
+   * instance to be thrown, so technically `parseError` will throw the object
+   * produced by the `new ExceptionClass(str, hash)` JavaScript expression.
+   *
+   * ---
+   *
+   * You can specify lexer options by setting / modifying the `.options` object
+   * of your Lexer instance. These options are available:
+   *
+   * (Options are permanent.)
+   *
+   *  yy: {
+   *      parseError: function(str, hash, ExceptionClass)
+   *                 optional: overrides the default `parseError` function.
+   *  }
+   *
+   *  lexer.options: {
+   *      pre_lex:  function()
+   *                 optional: is invoked before the lexer is invoked to produce
+   * another token. `this` refers to the Lexer object. post_lex:
+   * function(token) { return token; } optional: is invoked when the lexer has
+   * produced a token `token`; this function can override the returned token
+   * value by returning another. When it does not return any (truthy) value, the
+   * lexer will return the original `token`. `this` refers to the Lexer object.
+   *
+   * WARNING: the next set of options are not meant to be changed. They echo the
+   * abilities of the lexer as per when it was compiled!
+   *
+   *      ranges: boolean
+   *                 optional: `true` ==> token location info will include a
+   * .range[] member. flex: boolean optional: `true` ==> flex-like lexing
+   * behaviour where the rules are tested exhaustively to find the longest
+   * match. backtrack_lexer: boolean optional: `true` ==> lexer regexes are
+   * tested in order and for invoked; the lexer terminates the scan when a token
+   * is returned by the action code. xregexp: boolean optional: `true` ==> lexer
+   * rule regexes are "extended regex format" requiring the `XRegExp` library.
+   * When this %option has not been specified at compile time, all lexer rule
+   * regexes have been written as standard JavaScript RegExp expressions.
+   *  }
+   */
+
+
+  var lexer = function() {
+    /**
+     * See also:
+     * http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508
+     * but we keep the prototype.constructor and prototype.name assignment lines
+     * too for compatibility with userland code which might access the derived
+     * class in a 'classic' way.
+     *
+     * @public
+     * @constructor
+     * @nocollapse
+     */
+    function JisonLexerError(msg, hash) {
+      Object.defineProperty(
+          this, 'name',
+          {enumerable: false, writable: false, value: 'JisonLexerError'});
+
+      if (msg == null) {
+        msg = '???';
+      }
+
+      Object.defineProperty(
+          this, 'message', {enumerable: false, writable: true, value: msg});
+
+      this.hash = hash;
+      var stacktrace;
+
+      if (hash && hash.exception instanceof Error) {
+        var ex2 = hash.exception;
+        this.message = ex2.message || msg;
+        stacktrace = ex2.stack;
+      }
+
+      if (!stacktrace) {
+        if (Error.hasOwnProperty('captureStackTrace')) {
+          // V8
+          Error.captureStackTrace(this, this.constructor);
+        } else {
+          stacktrace = new Error(msg).stack;
+        }
+      }
+
+      if (stacktrace) {
+        Object.defineProperty(
+            this, 'stack',
+            {enumerable: false, writable: false, value: stacktrace});
+      }
+    }
+
+    if (typeof Object.setPrototypeOf === 'function') {
+      Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype);
+    } else {
+      JisonLexerError.prototype = Object.create(Error.prototype);
+    }
+
+    JisonLexerError.prototype.constructor = JisonLexerError;
+    JisonLexerError.prototype.name = 'JisonLexerError';
+
+    var lexer = {
+
+      // Code Generator Information Report
+      // ---------------------------------
+      //
+      // Options:
+      //
+      //   backtracking: .................... false
+      //   location.ranges: ................. false
+      //   location line+column tracking: ... true
+      //
+      //
+      // Forwarded Parser Analysis flags:
+      //
+      //   uses yyleng: ..................... false
+      //   uses yylineno: ................... false
+      //   uses yytext: ..................... false
+      //   uses yylloc: ..................... false
+      //   uses lexer values: ............... true / true
+      //   location tracking: ............... false
+      //   location assignment: ............. false
+      //
+      //
+      // Lexer Analysis flags:
+      //
+      //   uses yyleng: ..................... ???
+      //   uses yylineno: ................... ???
+      //   uses yytext: ..................... ???
+      //   uses yylloc: ..................... ???
+      //   uses ParseError API: ............. ???
+      //   uses yyerror: .................... ???
+      //   uses location tracking & editing:  ???
+      //   uses more() API: ................. ???
+      //   uses unput() API: ................ ???
+      //   uses reject() API: ............... ???
+      //   uses less() API: ................. ???
+      //   uses display APIs pastInput(), upcomingInput(), showPosition():
+      //        ............................. ???
+      //   uses describeYYLLOC() API: ....... ???
+      //
+      // --------- END OF REPORT -----------
+
+      EOF: 1,
+      ERROR: 2,
+
+      // JisonLexerError: JisonLexerError,        /// <-- injected by the code
+      // generator
+
+      // options: {},                             /// <-- injected by the code
+      // generator
+
+      // yy: ...,                                 /// <-- injected by setInput()
+
+      __currentRuleSet__: null,  /// INTERNAL USE ONLY: internal rule set cache
+                                 /// for the current lexer state
+
+      __error_infos: [],  /// INTERNAL USE ONLY: the set of lexErrorInfo objects
+                          /// created since the last cleanup
+      __decompressed:
+          false,    /// INTERNAL USE ONLY: mark whether the lexer instance has
+                    /// been 'unfolded' completely and is now ready for use
+      done: false,  /// INTERNAL USE ONLY
+      _backtrack: false,             /// INTERNAL USE ONLY
+      _input: '',                    /// INTERNAL USE ONLY
+      _more: false,                  /// INTERNAL USE ONLY
+      _signaled_error_token: false,  /// INTERNAL USE ONLY
+      conditionStack: [],  /// INTERNAL USE ONLY; managed via `pushState()`,
+                           /// `popState()`, `topState()` and `stateStackSize()`
+      match:
+          '',  /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input
+               /// which has been matched so far for the lexer token under
+               /// construction. `match` is identical to `yytext` except that
+               /// this one still contains the matched input string after
+               /// `lexer.performAction()` has been invoked, where userland code
+               /// MAY have changed/replaced the `yytext` value entirely!
+      matched: '',     /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks
+                       /// entire input which has been matched so far
+      matches: false,  /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks
+                       /// RE match result for last (successful) match attempt
+      yytext: '',  /// ADVANCED USE ONLY: tracks input which has been matched so
+                   /// far for the lexer token under construction; this value is
+                   /// transferred to the parser as the 'token value' when the
+                   /// parser consumes the lexer token produced through a call
+                   /// to the `lex()` API.
+      offset: 0,   /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the
+                   /// 'cursor position' in the input string, i.e. the number of
+                   /// characters matched so far
+      yyleng: 0,   /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of
+                   /// matched input for the token under construction (`yytext`)
+      yylineno: 0,  /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line
+                    /// number' at which the token under construction is located
+      yylloc: null,  /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks
+                     /// location info (lines + columns) for the token under
+                     /// construction
+
+      /**
+       * INTERNAL USE: construct a suitable error info hash object instance for
+       * `parseError`.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      constructLexErrorInfo: function lexer_constructLexErrorInfo(
+          msg, recoverable, show_input_position) {
+        msg = '' + msg;
+
+        // heuristic to determine if the error message already contains a
+        // (partial) source code dump as produced by either `showPosition()` or
+        // `prettyPrintRange()`:
+        if (!show_input_position) {
+          show_input_position =
+              !(msg.indexOf('\n') > 0 && msg.indexOf('^') > 0);
+        }
+
+        if (this.yylloc && show_input_position) {
+          if (typeof this.prettyPrintRange === 'function') {
+            var prettySrc = this.prettyPrintRange(this.yylloc);
+
+            if (!/\n\s*$/.test(msg)) {
+              msg += '\n';
+            }
+
+            msg += '\n  Erroneous area:\n' + this.prettyPrintRange(this.yylloc);
+          } else if (typeof this.showPosition === 'function') {
+            var posStr = this.showPosition();
+
+            if (posStr) {
+              if (msg.length && msg[msg.length - 1] !== '\n' &&
+                  posStr[0] !== '\n') {
+                msg += '\n' + posStr;
+              } else {
+                msg += posStr;
+              }
+            }
+          }
+        }
+
+        /** @constructor */
+        var pei = {
+          errStr: msg,
+          recoverable: !!recoverable,
+          text: this.match,  // This one MAY be empty; userland code should use
+                             // the `upcomingInput` API to obtain more text
+                             // which follows the 'lexer cursor position'...
+          token: null,
+          line: this.yylineno,
+          loc: this.yylloc,
+          yy: this.yy,
+          lexer: this,
+
+          /**
+           * and make sure the error info doesn't stay due to potential
+           * ref cycle via userland code manipulations.
+           * These would otherwise all be memory leak opportunities!
+           *
+           * Note that only array and object references are nuked as those
+           * constitute the set of elements which can produce a cyclic ref.
+           * The rest of the members is kept intact as they are harmless.
+           *
+           * @public
+           * @this {LexErrorInfo}
+           */
+          destroy: function destructLexErrorInfo() {
+            // remove cyclic references added to error info:
+            // info.yy = null;
+            // info.lexer = null;
+            // ...
+            var rec = !!this.recoverable;
+
+            for (var key in this) {
+              if (this.hasOwnProperty(key) && typeof key === 'object') {
+                this[key] = undefined;
+              }
+            }
+
+            this.recoverable = rec;
+          }
+        };
+
+        // track this instance so we can `destroy()` it once we deem it
+        // superfluous and ready for garbage collection!
+        this.__error_infos.push(pei);
+
+        return pei;
+      },
+
+      /**
+       * handler which is invoked when a lexer error occurs.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      parseError: function lexer_parseError(str, hash, ExceptionClass) {
+        if (!ExceptionClass) {
+          ExceptionClass = this.JisonLexerError;
+        }
+
+        if (this.yy) {
+          if (this.yy.parser &&
+              typeof this.yy.parser.parseError === 'function') {
+            return this.yy.parser.parseError.call(
+                       this, str, hash, ExceptionClass) ||
+                this.ERROR;
+          } else if (typeof this.yy.parseError === 'function') {
+            return this.yy.parseError.call(this, str, hash, ExceptionClass) ||
+                this.ERROR;
+          }
+        }
+
+        throw new ExceptionClass(str, hash);
+      },
+
+      /**
+       * method which implements `yyerror(str, ...args)` functionality for use
+       * inside lexer actions.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      yyerror: function yyError(str /*, ...args */) {
+        var linenoMsg = '';
+
+        if (this.yylloc) {
+          linenoMsg = ' on line ' + (this.yylineno + 1);
+        }
+
+        var p = this.constructLexErrorInfo(
+            'Lexical error' + linenoMsg + ': ' + str,
+            this.options.lexerErrorsAreRecoverable);
+
+        // Add any extra args to the hash under the name
+        // `extra_error_attributes`:
+        var args = Array.prototype.slice.call(arguments, 1);
+
+        if (args.length) {
+          p.extra_error_attributes = args;
+        }
+
+        return this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR;
+      },
+
+      /**
+       * final cleanup function for when we have completed lexing the input;
+       * make it an API so that external code can use this one once userland
+       * code has decided it's time to destroy any lingering lexer error
+       * hash object instances and the like: this function helps to clean
+       * up these constructs, which *may* carry cyclic references which would
+       * otherwise prevent the instances from being properly and timely
+       * garbage-collected, i.e. this function helps prevent memory leaks!
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) {
+        // prevent lingering circular references from causing memory leaks:
+        this.setInput('', {});
+
+        // nuke the error hash info instances created during this run.
+        // Userland code must COPY any data/references
+        // in the error hash instance(s) it is more permanently interested in.
+        if (!do_not_nuke_errorinfos) {
+          for (var i = this.__error_infos.length - 1; i >= 0; i--) {
+            var el = this.__error_infos[i];
+
+            if (el && typeof el.destroy === 'function') {
+              el.destroy();
+            }
+          }
+
+          this.__error_infos.length = 0;
+        }
+
+        return this;
+      },
+
+      /**
+       * clear the lexer token context; intended for internal use only
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      clear: function lexer_clear() {
+        this.yytext = '';
+        this.yyleng = 0;
+        this.match = '';
+
+        // - DO NOT reset `this.matched`
+        this.matches = false;
+
+        this._more = false;
+        this._backtrack = false;
+        var col = (this.yylloc ? this.yylloc.last_column : 0);
+
+        this.yylloc = {
+          first_line: this.yylineno + 1,
+          first_column: col,
+          last_line: this.yylineno + 1,
+          last_column: col,
+          range: [this.offset, this.offset]
+        };
+      },
+
+      /**
+       * resets the lexer, sets new input
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      setInput: function lexer_setInput(input, yy) {
+        this.yy = yy || this.yy || {};
+
+        // also check if we've fully initialized the lexer instance,
+        // including expansion work to be done to go from a loaded
+        // lexer to a usable lexer:
+        if (!this.__decompressed) {
+          // step 1: decompress the regex list:
+          var rules = this.rules;
+
+          for (var i = 0, len = rules.length; i < len; i++) {
+            var ruleRe = rules[i];
+
+            // compression: is the RE an xref to another RE slot in the rules[]
+            // table?
+            if (typeof ruleRe === 'number') {
+              rules[i] = rules[ruleRe];
+            }
+          }
+
+          // step 2: unfold the conditions[] set to make these ready for use:
+          var conditions = this.conditions;
+
+          for (var k in conditions) {
+            var spec = conditions[k];
+            var ruleIds = spec.rules;
+            var len = ruleIds.length;
+            var ruleRegexes =
+                new Array(len + 1);  // slot 0 is unused; we use a 1-based index
+                                     // approach here to keep the hottest code
+                                     // in `lexer_next()` fast and simple!
+            var ruleNewIds = new Array(len + 1);
+
+            for (var i = 0; i < len; i++) {
+              var idx = ruleIds[i];
+              var ruleRe = rules[idx];
+              ruleRegexes[i + 1] = ruleRe;
+              ruleNewIds[i + 1] = idx;
+            }
+
+            spec.rules = ruleNewIds;
+            spec.__ruleRegexes = ruleRegexes;
+            spec.__rule_count = len;
+          }
+
+          this.__decompressed = true;
+        }
+
+        this._input = input || '';
+        this.clear();
+        this._signaled_error_token = false;
+        this.done = false;
+        this.yylineno = 0;
+        this.matched = '';
+        this.conditionStack = ['INITIAL'];
+        this.__currentRuleSet__ = null;
+
+        this.yylloc = {
+          first_line: 1,
+          first_column: 0,
+          last_line: 1,
+          last_column: 0,
+          range: [0, 0]
+        };
+
+        this.offset = 0;
+        return this;
+      },
+
+      /**
+       * edit the remaining input via user-specified callback.
+       * This can be used to forward-adjust the input-to-parse,
+       * e.g. inserting macro expansions and alike in the
+       * input which has yet to be lexed.
+       * The behaviour of this API contrasts the `unput()` et al
+       * APIs as those act on the *consumed* input, while this
+       * one allows one to manipulate the future, without impacting
+       * the current `yyloc` cursor location or any history.
+       *
+       * Use this API to help implement C-preprocessor-like
+       * `#include` statements, etc.
+       *
+       * The provided callback must be synchronous and is
+       * expected to return the edited input (string).
+       *
+       * The `cpsArg` argument value is passed to the callback
+       * as-is.
+       *
+       * `callback` interface:
+       * `function callback(input, cpsArg)`
+       *
+       * - `input` will carry the remaining-input-to-lex string
+       *   from the lexer.
+       * - `cpsArg` is `cpsArg` passed into this API.
+       *
+       * The `this` reference for the callback will be set to
+       * reference this lexer instance so that userland code
+       * in the callback can easily and quickly access any lexer
+       * API.
+       *
+       * When the callback returns a non-string-type falsey value,
+       * we assume the callback did not edit the input and we
+       * will using the input as-is.
+       *
+       * When the callback returns a non-string-type value, it
+       * is converted to a string for lexing via the `"" + retval`
+       * operation. (See also why:
+       * http://2ality.com/2012/03/converting-to-string.html
+       * -- that way any returned object's `toValue()` and `toString()`
+       * methods will be invoked in a proper/desirable order.)
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      editRemainingInput: function lexer_editRemainingInput(callback, cpsArg) {
+        var rv = callback.call(this, this._input, cpsArg);
+
+        if (typeof rv !== 'string') {
+          if (rv) {
+            this._input = '' + rv;
+          }
+          // else: keep `this._input` as is.
+        } else {
+          this._input = rv;
+        }
+
+        return this;
+      },
+
+      /**
+       * consumes and returns one char from the input
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      input: function lexer_input() {
+        if (!this._input) {
+          // this.done = true;    -- don't set `done` as we want the
+          // lex()/next() API to be able to produce one custom EOF token match
+          // after this anyhow. (lexer can match special <<EOF>> tokens and
+          // perform user action code for a <<EOF>> match, but only does so
+          // *once*)
+          return null;
+        }
+
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+
+        // Count the linenumber up when we hit the LF (or a stand-alone CR).
+        // On CRLF, the linenumber is incremented when you fetch the CR or the
+        // CRLF combo and we advance immediately past the LF as well, returning
+        // both together as if it was all a single 'character' only.
+        var sliceLen = 1;
+
+        var lines = false;
+
+        if (ch === '\n') {
+          lines = true;
+        } else if (ch === '\r') {
+          lines = true;
+          var ch2 = this._input[1];
+
+          if (ch2 === '\n') {
+            sliceLen++;
+            ch += ch2;
+            this.yytext += ch2;
+            this.yyleng++;
+            this.offset++;
+            this.match += ch2;
+            this.matched += ch2;
+            this.yylloc.range[1]++;
+          }
+        }
+
+        if (lines) {
+          this.yylineno++;
+          this.yylloc.last_line++;
+          this.yylloc.last_column = 0;
+        } else {
+          this.yylloc.last_column++;
+        }
+
+        this.yylloc.range[1]++;
+        this._input = this._input.slice(sliceLen);
+        return ch;
+      },
+
+      /**
+       * unshifts one char (or an entire string) into the input
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      unput: function lexer_unput(ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        this.yyleng = this.yytext.length;
+        this.offset -= len;
+        this.match = this.match.substr(0, this.match.length - len);
+        this.matched = this.matched.substr(0, this.matched.length - len);
+
+        if (lines.length > 1) {
+          this.yylineno -= lines.length - 1;
+          this.yylloc.last_line = this.yylineno + 1;
+
+          // Get last entirely matched line into the `preLines[]` array's
+          // last index slot; we don't mind when other previously
+          // matched lines end up in the array too.
+          var pre = this.match;
+
+          var preLines = pre.split(/(?:\r\n?|\n)/g);
+
+          if (preLines.length === 1) {
+            pre = this.matched;
+            preLines = pre.split(/(?:\r\n?|\n)/g);
+          }
+
+          this.yylloc.last_column = preLines[preLines.length - 1].length;
+        } else {
+          this.yylloc.last_column -= len;
+        }
+
+        this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng;
+        this.done = false;
+        return this;
+      },
+
+      /**
+       * cache matched text and append it on next action
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      more: function lexer_more() {
+        this._more = true;
+        return this;
+      },
+
+      /**
+       * signal the lexer that this rule fails to match the input, so the
+       * next matching rule (regex) should be tested instead.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      reject: function lexer_reject() {
+        if (this.options.backtrack_lexer) {
+          this._backtrack = true;
+        } else {
+          // when the `parseError()` call returns, we MUST ensure that the error
+          // is registered. We accomplish this by signaling an 'error' token to
+          // be produced for the current
+          // `.lex()` run.
+          var linenoMsg = '';
+
+          if (this.yylloc) {
+            linenoMsg = ' on line ' + (this.yylineno + 1);
+          }
+
+          var p = this.constructLexErrorInfo(
+              'Lexical error' + linenoMsg +
+                  ': You can only invoke reject() in the lexer when the ' +
+                  'lexer is of the backtracking persuasion ' +
+                  '(options.backtrack_lexer = true).',
+              false);
+
+          this._signaled_error_token =
+              this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR;
+        }
+
+        return this;
+      },
+
+      /**
+       * retain first n characters of the match
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      less: function lexer_less(n) {
+        return this.unput(this.match.slice(n));
+      },
+
+      /**
+       * return (part of the) already matched input, i.e. for error
+       * messages.
+       *
+       * Limit the returned string length to `maxSize` (default: 20).
+       *
+       * Limit the returned string to the `maxLines` number of lines of
+       * input (default: 1).
+       *
+       * Negative limit values equal *unlimited*.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      pastInput: function lexer_pastInput(maxSize, maxLines) {
+        var past =
+            this.matched.substring(0, this.matched.length - this.match.length);
+
+        if (maxSize < 0) {
+          maxSize = past.length;
+        } else if (!maxSize) {
+          maxSize = 20;
+        }
+
+        if (maxLines < 0) {
+          maxLines = past.length;
+        } else if (!maxLines) {
+          maxLines = 1;
+        }
+
+        // `substr` anticipation: treat \r\n as a single character and take a
+        // little more than necessary so that we can still properly check
+        // against maxSize after we've transformed and limited the newLines in
+        // here:
+        past = past.substr(-maxSize * 2 - 2);
+
+        // now that we have a significantly reduced string to process, transform
+        // the newlines and chop them, then limit them:
+        var a = past.replace(/\r\n|\r/g, '\n').split('\n');
+
+        a = a.slice(-maxLines);
+        past = a.join('\n');
+
+        // When, after limiting to maxLines, we still have too much to return,
+        // do add an ellipsis prefix...
+        if (past.length > maxSize) {
+          past = '...' + past.substr(-maxSize);
+        }
+
+        return past;
+      },
+
+      /**
+       * return (part of the) upcoming input, i.e. for error messages.
+       *
+       * Limit the returned string length to `maxSize` (default: 20).
+       *
+       * Limit the returned string to the `maxLines` number of lines of input
+       * (default: 1).
+       *
+       * Negative limit values equal *unlimited*.
+       *
+       * > ### NOTE ###
+       * >
+       * > *"upcoming input"* is defined as the whole of the both
+       * > the *currently lexed* input, together with any remaining input
+       * > following that. *"currently lexed"* input is the input
+       * > already recognized by the lexer but not yet returned with
+       * > the lexer token. This happens when you are invoking this API
+       * > from inside any lexer rule action code block.
+       * >
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      upcomingInput: function lexer_upcomingInput(maxSize, maxLines) {
+        var next = this.match;
+
+        if (maxSize < 0) {
+          maxSize = next.length + this._input.length;
+        } else if (!maxSize) {
+          maxSize = 20;
+        }
+
+        if (maxLines < 0) {
+          maxLines = maxSize;
+        } else if (!maxLines) {
+          maxLines = 1;
+        }
+
+        // `substring` anticipation: treat \r\n as a single character and take a
+        // little more than necessary so that we can still properly check
+        // against maxSize after we've transformed and limited the newLines in
+        // here:
+        if (next.length < maxSize * 2 + 2) {
+          next += this._input.substring(
+              0, maxSize * 2 + 2);  // substring is faster on Chrome/V8
+        }
+
+        // now that we have a significantly reduced string to process, transform
+        // the newlines and chop them, then limit them:
+        var a = next.replace(/\r\n|\r/g, '\n').split('\n');
+
+        a = a.slice(0, maxLines);
+        next = a.join('\n');
+
+        // When, after limiting to maxLines, we still have too much to return,
+        // do add an ellipsis postfix...
+        if (next.length > maxSize) {
+          next = next.substring(0, maxSize) + '...';
+        }
+
+        return next;
+      },
+
+      /**
+       * return a string which displays the character position where the
+       * lexing error occurred, i.e. for error messages
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      showPosition: function lexer_showPosition(maxPrefix, maxPostfix) {
+        var pre = this.pastInput(maxPrefix).replace(/\s/g, ' ');
+        var c = new Array(pre.length + 1).join('-');
+        return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' +
+            c + '^';
+      },
+
+      /**
+       * return an YYLLOC info object derived off the given context (actual,
+       * preceding, following, current). Use this method when the given `actual`
+       * location is not guaranteed to exist (i.e. when it MAY be NULL) and you
+       * MUST have a valid location info object anyway: then we take the given
+       * context of the `preceding` and `following` locations, IFF those are
+       * available, and reconstruct the `actual` location info from those. If
+       * this fails, the heuristic is to take the `current` location, IFF
+       * available. If this fails as well, we assume the sought location is
+       * at/around the current lexer position and then produce that one as a
+       * response. DO NOTE that these heuristic/derived location info values MAY
+       * be inaccurate!
+       *
+       * NOTE: `deriveLocationInfo()` ALWAYS produces a location info object
+       * *copy* of `actual`, not just a *reference* hence all input location
+       * objects can be assumed to be 'constant' (function has no side-effects).
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      deriveLocationInfo: function lexer_deriveYYLLOC(
+          actual, preceding, following, current) {
+        var loc = {
+          first_line: 1,
+          first_column: 0,
+          last_line: 1,
+          last_column: 0,
+          range: [0, 0]
+        };
+
+        if (actual) {
+          loc.first_line = actual.first_line | 0;
+          loc.last_line = actual.last_line | 0;
+          loc.first_column = actual.first_column | 0;
+          loc.last_column = actual.last_column | 0;
+
+          if (actual.range) {
+            loc.range[0] = actual.range[0] | 0;
+            loc.range[1] = actual.range[1] | 0;
+          }
+        }
+
+        if (loc.first_line <= 0 || loc.last_line < loc.first_line) {
+          // plan B: heuristic using preceding and following:
+          if (loc.first_line <= 0 && preceding) {
+            loc.first_line = preceding.last_line | 0;
+            loc.first_column = preceding.last_column | 0;
+
+            if (preceding.range) {
+              loc.range[0] = actual.range[1] | 0;
+            }
+          }
+
+          if ((loc.last_line <= 0 || loc.last_line < loc.first_line) &&
+              following) {
+            loc.last_line = following.first_line | 0;
+            loc.last_column = following.first_column | 0;
+
+            if (following.range) {
+              loc.range[1] = actual.range[0] | 0;
+            }
+          }
+
+          // plan C?: see if the 'current' location is useful/sane too:
+          if (loc.first_line <= 0 && current &&
+              (loc.last_line <= 0 || current.last_line <= loc.last_line)) {
+            loc.first_line = current.first_line | 0;
+            loc.first_column = current.first_column | 0;
+
+            if (current.range) {
+              loc.range[0] = current.range[0] | 0;
+            }
+          }
+
+          if (loc.last_line <= 0 && current &&
+              (loc.first_line <= 0 || current.first_line >= loc.first_line)) {
+            loc.last_line = current.last_line | 0;
+            loc.last_column = current.last_column | 0;
+
+            if (current.range) {
+              loc.range[1] = current.range[1] | 0;
+            }
+          }
+        }
+
+        // sanitize: fix last_line BEFORE we fix first_line as we use the 'raw'
+        // value of the latter or plan D heuristics to produce a 'sensible'
+        // last_line value:
+        if (loc.last_line <= 0) {
+          if (loc.first_line <= 0) {
+            loc.first_line = this.yylloc.first_line;
+            loc.last_line = this.yylloc.last_line;
+            loc.first_column = this.yylloc.first_column;
+            loc.last_column = this.yylloc.last_column;
+            loc.range[0] = this.yylloc.range[0];
+            loc.range[1] = this.yylloc.range[1];
+          } else {
+            loc.last_line = this.yylloc.last_line;
+            loc.last_column = this.yylloc.last_column;
+            loc.range[1] = this.yylloc.range[1];
+          }
+        }
+
+        if (loc.first_line <= 0) {
+          loc.first_line = loc.last_line;
+          loc.first_column = 0;  // loc.last_column;
+          loc.range[1] = loc.range[0];
+        }
+
+        if (loc.first_column < 0) {
+          loc.first_column = 0;
+        }
+
+        if (loc.last_column < 0) {
+          loc.last_column = (loc.first_column > 0 ? loc.first_column : 80);
+        }
+
+        return loc;
+      },
+
+      /**
+       * return a string which displays the lines & columns of input which are
+       * referenced by the given location info range, plus a few lines of
+       * context.
+       *
+       * This function pretty-prints the indicated section of the input, with
+       * line numbers and everything!
+       *
+       * This function is very useful to provide highly readable error reports,
+       * while the location range may be specified in various flexible ways:
+       *
+       * - `loc` is the location info object which references the area which
+       * should be displayed and 'marked up': these lines & columns of text are
+       * marked up by `^` characters below each character in the entire input
+       * range.
+       *
+       * - `context_loc` is the *optional* location info object which instructs
+       * this pretty-printer how much *leading* context should be displayed
+       * alongside the area referenced by `loc`. This can help provide context
+       * for the displayed error, etc.
+       *
+       *   When this location info is not provided, a default context of 3 lines
+       * is used.
+       *
+       * - `context_loc2` is another *optional* location info object, which
+       * serves a similar purpose to `context_loc`: it specifies the amount of
+       * *trailing* context lines to display in the pretty-print output.
+       *
+       *   When this location info is not provided, a default context of 1 line
+       * only is used.
+       *
+       * Special Notes:
+       *
+       * - when the `loc`-indicated range is very large (about 5 lines or more),
+       * then only the first and last few lines of this block are printed while
+       * a
+       *   `...continued...` message will be printed between them.
+       *
+       *   This serves the purpose of not printing a huge amount of text when
+       * the `loc` range happens to be huge: this way a manageable & readable
+       * output results for arbitrary large ranges.
+       *
+       * - this function can display lines of input which whave not yet been
+       * lexed. `prettyPrintRange()` can access the entire input!
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      prettyPrintRange: function lexer_prettyPrintRange(
+          loc, context_loc, context_loc2) {
+        loc = this.deriveLocationInfo(loc, context_loc, context_loc2);
+        const CONTEXT = 3;
+        const CONTEXT_TAIL = 1;
+        const MIN_VIS_LINE = 2;
+        var input = this.matched + this._input;
+        var lines = input.split('\n');
+        var l0 = Math.max(
+            1,
+            (context_loc ? context_loc.first_line : loc.first_line - CONTEXT));
+        var l1 = Math.max(
+            1,
+            (context_loc2 ? context_loc2.last_line :
+                            loc.last_line + CONTEXT_TAIL));
+        var linenoDisplayWidth = 1 + Math.log10(l1 | 1) | 0;
+        var wsPrefix = new Array(linenoDisplayWidth).join(' ');
+        var nonemptyLineIndexes = [];
+
+        var rv = lines.slice(l0 - 1, l1 + 1)
+                     .map(function injectLineNumber(line, index) {
+                       var lno = index + l0;
+                       var lnoPfx =
+                           (wsPrefix + lno).substr(-linenoDisplayWidth);
+                       var rv = lnoPfx + ': ' + line;
+                       var errpfx = new Array(linenoDisplayWidth + 1).join('^');
+                       var offset = 2 + 1;
+                       var len = 0;
+
+                       if (lno === loc.first_line) {
+                         offset += loc.first_column;
+
+                         len = Math.max(
+                             2,
+                             ((lno === loc.last_line ? loc.last_column :
+                                                       line.length)) -
+                                 loc.first_column + 1);
+                       } else if (lno === loc.last_line) {
+                         len = Math.max(2, loc.last_column + 1);
+                       } else if (lno > loc.first_line && lno < loc.last_line) {
+                         len = Math.max(2, line.length + 1);
+                       }
+
+                       if (len) {
+                         var lead = new Array(offset).join('.');
+                         var mark = new Array(len).join('^');
+                         rv += '\n' + errpfx + lead + mark;
+
+                         if (line.trim().length > 0) {
+                           nonemptyLineIndexes.push(index);
+                         }
+                       }
+
+                       rv = rv.replace(/\t/g, ' ');
+                       return rv;
+                     });
+
+        // now make sure we don't print an overly large amount of error area:
+        // limit it to the top and bottom line count:
+        if (nonemptyLineIndexes.length > 2 * MIN_VIS_LINE) {
+          var clipStart = nonemptyLineIndexes[MIN_VIS_LINE - 1] + 1;
+          var clipEnd =
+              nonemptyLineIndexes[nonemptyLineIndexes.length - MIN_VIS_LINE] -
+              1;
+          var intermediateLine = new Array(linenoDisplayWidth + 1).join(' ') +
+              '  (...continued...)';
+          intermediateLine += '\n' +
+              new Array(linenoDisplayWidth + 1).join('-') +
+              '  (---------------)';
+          rv.splice(clipStart, clipEnd - clipStart + 1, intermediateLine);
+        }
+
+        return rv.join('\n');
+      },
+
+      /**
+       * helper function, used to produce a human readable description as a
+       * string, given the input `yylloc` location object.
+       *
+       * Set `display_range_too` to TRUE to include the string character index
+       * position(s) in the description if the `yylloc.range` is available.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      describeYYLLOC: function lexer_describe_yylloc(
+          yylloc, display_range_too) {
+        var l1 = yylloc.first_line;
+        var l2 = yylloc.last_line;
+        var c1 = yylloc.first_column;
+        var c2 = yylloc.last_column;
+        var dl = l2 - l1;
+        var dc = c2 - c1;
+        var rv;
+
+        if (dl === 0) {
+          rv = 'line ' + l1 + ', ';
+
+          if (dc <= 1) {
+            rv += 'column ' + c1;
+          } else {
+            rv += 'columns ' + c1 + ' .. ' + c2;
+          }
+        } else {
+          rv = 'lines ' + l1 + '(column ' + c1 + ') .. ' + l2 + '(column ' +
+              c2 + ')';
+        }
+
+        if (yylloc.range && display_range_too) {
+          var r1 = yylloc.range[0];
+          var r2 = yylloc.range[1] - 1;
+
+          if (r2 <= r1) {
+            rv += ' {String Offset: ' + r1 + '}';
+          } else {
+            rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}';
+          }
+        }
+
+        return rv;
+      },
+
+      /**
+       * test the lexed token: return FALSE when not a match, otherwise return
+       * token.
+       *
+       * `match` is supposed to be an array coming out of a regex match, i.e.
+       * `match[0]` contains the actually matched text string.
+       *
+       * Also move the input cursor forward and update the match collectors:
+       *
+       * - `yytext`
+       * - `yyleng`
+       * - `match`
+       * - `matches`
+       * - `yylloc`
+       * - `offset`
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      test_match: function lexer_test_match(match, indexed_rule) {
+        var token, lines, backup, match_str, match_str_len;
+
+        if (this.options.backtrack_lexer) {
+          // save context
+          backup = {
+            yylineno: this.yylineno,
+
+            yylloc: {
+              first_line: this.yylloc.first_line,
+              last_line: this.yylloc.last_line,
+              first_column: this.yylloc.first_column,
+              last_column: this.yylloc.last_column,
+              range: this.yylloc.range.slice(0)
+            },
+
+            yytext: this.yytext,
+            match: this.match,
+            matches: this.matches,
+            matched: this.matched,
+            yyleng: this.yyleng,
+            offset: this.offset,
+            _more: this._more,
+            _input: this._input,
+
+            //_signaled_error_token: this._signaled_error_token,
+            yy: this.yy,
+
+            conditionStack: this.conditionStack.slice(0),
+            done: this.done
+          };
+        }
+
+        match_str = match[0];
+        match_str_len = match_str.length;
+
+        // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !==
+        // -1) {
+        lines = match_str.split(/(?:\r\n?|\n)/g);
+
+        if (lines.length > 1) {
+          this.yylineno += lines.length - 1;
+          this.yylloc.last_line = this.yylineno + 1;
+          this.yylloc.last_column = lines[lines.length - 1].length;
+        } else {
+          this.yylloc.last_column += match_str_len;
+        }
+
+        // }
+        this.yytext += match_str;
+
+        this.match += match_str;
+        this.matched += match_str;
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        this.yylloc.range[1] += match_str_len;
+
+        // previous lex rules MAY have invoked the `more()` API rather than
+        // producing a token: those rules will already have moved this `offset`
+        // forward matching their match lengths, hence we must only add our own
+        // match length now:
+        this.offset += match_str_len;
+
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match_str_len);
+
+        // calling this method:
+        //
+        //   function lexer__performAction(yy, yyrulenumber, YY_START) {...}
+        token = this.performAction.call(
+            this, this.yy, indexed_rule,
+            this.conditionStack[this.conditionStack.length - 1] /* = YY_START */
+        );
+
+        // otherwise, when the action codes are all simple return token
+        // statements:
+        // token = this.simpleCaseActionClusters[indexed_rule];
+
+        if (this.done && this._input) {
+          this.done = false;
+        }
+
+        if (token) {
+          return token;
+        } else if (this._backtrack) {
+          // recover context
+          for (var k in backup) {
+            this[k] = backup[k];
+          }
+
+          this.__currentRuleSet__ = null;
+          return false;  // rule action called reject() implying the next rule
+                         // should be tested instead.
+        } else if (this._signaled_error_token) {
+          // produce one 'error' token as `.parseError()` in `reject()`
+          // did not guarantee a failure signal by throwing an exception!
+          token = this._signaled_error_token;
+
+          this._signaled_error_token = false;
+          return token;
+        }
+
+        return false;
+      },
+
+      /**
+       * return next match in input
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      next: function lexer_next() {
+        if (this.done) {
+          this.clear();
+          return this.EOF;
+        }
+
+        if (!this._input) {
+          this.done = true;
+        }
+
+        var token, match, tempMatch, index;
+
+        if (!this._more) {
+          this.clear();
+        }
+
+        var spec = this.__currentRuleSet__;
+
+        if (!spec) {
+          // Update the ruleset cache as we apparently encountered a state
+          // change or just started lexing. The cache is set up for fast lookup
+          // -- we assume a lexer will switch states much less often than it
+          // will invoke the `lex()` token-producing API and related APIs, hence
+          // caching the set for direct access helps speed up those activities a
+          // tiny bit.
+          spec = this.__currentRuleSet__ = this._currentRules();
+
+          // Check whether a *sane* condition has been pushed before: this makes
+          // the lexer robust against user-programmer bugs such as
+          // https://github.com/zaach/jison-lex/issues/19
+          if (!spec || !spec.rules) {
+            var linenoMsg = '';
+
+            if (this.options.trackPosition) {
+              linenoMsg = ' on line ' + (this.yylineno + 1);
+            }
+
+            var p = this.constructLexErrorInfo(
+                'Internal lexer engine error' + linenoMsg +
+                    ': The lex grammar programmer pushed a non-existing ' +
+                    'condition name "' + this.topState() +
+                    '"; this is a fatal error and should be reported to the ' +
+                    'application programmer team!',
+                false);
+
+            // produce one 'error' token until this situation has been resolved,
+            // most probably by parse termination!
+            return this.parseError(p.errStr, p, this.JisonLexerError) ||
+                this.ERROR;
+          }
+        }
+
+        var ruleIds = spec.rules;
+        var regexes = spec.__ruleRegexes;
+        var len = spec.__rule_count;
+
+        // Note: the arrays are 1-based, while `len` itself is a valid index,
+        // hence the non-standard less-or-equal check in the next loop
+        // condition!
+        for (var i = 1; i <= len; i++) {
+          tempMatch = this._input.match(regexes[i]);
+
+          if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+            match = tempMatch;
+            index = i;
+
+            if (this.options.backtrack_lexer) {
+              token = this.test_match(tempMatch, ruleIds[i]);
+
+              if (token !== false) {
+                return token;
+              } else if (this._backtrack) {
+                match = undefined;
+                continue;  // rule action called reject() implying a rule
+                           // MISmatch.
+              } else {
+                // else: this is a lexer rule which consumes input without
+                // producing a token (e.g. whitespace)
+                return false;
+              }
+            } else if (!this.options.flex) {
+              break;
+            }
+          }
+        }
+
+        if (match) {
+          token = this.test_match(match, ruleIds[index]);
+
+          if (token !== false) {
+            return token;
+          }
+
+          // else: this is a lexer rule which consumes input without producing a
+          // token (e.g. whitespace)
+          return false;
+        }
+
+        if (!this._input) {
+          this.done = true;
+          this.clear();
+          return this.EOF;
+        } else {
+          var linenoMsg = '';
+
+          if (this.options.trackPosition) {
+            linenoMsg = ' on line ' + (this.yylineno + 1);
+          }
+
+          var p = this.constructLexErrorInfo(
+              'Lexical error' + linenoMsg + ': Unrecognized text.',
+              this.options.lexerErrorsAreRecoverable);
+
+          var pendingInput = this._input;
+          var activeCondition = this.topState();
+          var conditionStackDepth = this.conditionStack.length;
+          token =
+              this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR;
+
+          if (token === this.ERROR) {
+            // we can try to recover from a lexer error that `parseError()` did
+            // not 'recover' for us by moving forward at least one character at
+            // a time IFF the (user-specified?) `parseError()` has not
+            // consumed/modified any pending input or changed state in the error
+            // handler:
+            if (!this.matches &&  // and make sure the input has been
+                                  // modified/consumed ...
+                pendingInput ===
+                    this._input &&  // ...or the lexer state has been modified
+                                    // significantly enough
+                // to merit a non-consuming error handling action right now.
+                activeCondition === this.topState() &&
+                conditionStackDepth === this.conditionStack.length) {
+              this.input();
+            }
+          }
+
+          return token;
+        }
+      },
+
+      /**
+       * return next match that has a token
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      lex: function lexer_lex() {
+        var r;
+
+        // allow the PRE/POST handlers set/modify the return token for maximum
+        // flexibility of the generated lexer:
+        if (typeof this.pre_lex === 'function') {
+          r = this.pre_lex.call(this, 0);
+        }
+
+        if (typeof this.options.pre_lex === 'function') {
+          // (also account for a userdef function which does not return any
+          // value: keep the token as is)
+          r = this.options.pre_lex.call(this, r) || r;
+        }
+
+        if (this.yy && typeof this.yy.pre_lex === 'function') {
+          // (also account for a userdef function which does not return any
+          // value: keep the token as is)
+          r = this.yy.pre_lex.call(this, r) || r;
+        }
+
+        while (!r) {
+          r = this.next();
+        }
+
+        if (this.yy && typeof this.yy.post_lex === 'function') {
+          // (also account for a userdef function which does not return any
+          // value: keep the token as is)
+          r = this.yy.post_lex.call(this, r) || r;
+        }
+
+        if (typeof this.options.post_lex === 'function') {
+          // (also account for a userdef function which does not return any
+          // value: keep the token as is)
+          r = this.options.post_lex.call(this, r) || r;
+        }
+
+        if (typeof this.post_lex === 'function') {
+          // (also account for a userdef function which does not return any
+          // value: keep the token as is)
+          r = this.post_lex.call(this, r) || r;
+        }
+
+        return r;
+      },
+
+      /**
+       * return next match that has a token. Identical to the `lex()` API but
+       * does not invoke any of the `pre_lex()` nor any of the `post_lex()`
+       * callbacks.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      fastLex: function lexer_fastLex() {
+        var r;
+
+        while (!r) {
+          r = this.next();
+        }
+
+        return r;
+      },
+
+      /**
+       * return info about the lexer state that can help a parser or other lexer
+       * API user to use the most efficient means available. This API is
+       * provided to aid run-time performance for larger systems which employ
+       * this lexer.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      canIUse: function lexer_canIUse() {
+        var rv = {
+          fastLex: !(typeof this.pre_lex === 'function' ||
+                     typeof this.options.pre_lex === 'function' ||
+                     this.yy && typeof this.yy.pre_lex === 'function' ||
+                     this.yy && typeof this.yy.post_lex === 'function' ||
+                     typeof this.options.post_lex === 'function' ||
+                     typeof this.post_lex === 'function') &&
+              typeof this.fastLex === 'function'
+        };
+
+        return rv;
+      },
+
+      /**
+       * backwards compatible alias for `pushState()`;
+       * the latter is symmetrical with `popState()` and we advise to use
+       * those APIs in any modern lexer code, rather than `begin()`.
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      begin: function lexer_begin(condition) {
+        return this.pushState(condition);
+      },
+
+      /**
+       * activates a new lexer condition state (pushes the new lexer
+       * condition state onto the condition stack)
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      pushState: function lexer_pushState(condition) {
+        this.conditionStack.push(condition);
+        this.__currentRuleSet__ = null;
+        return this;
+      },
+
+      /**
+       * pop the previously active lexer condition state off the condition
+       * stack
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      popState: function lexer_popState() {
+        var n = this.conditionStack.length - 1;
+
+        if (n > 0) {
+          this.__currentRuleSet__ = null;
+          return this.conditionStack.pop();
+        } else {
+          return this.conditionStack[0];
+        }
+      },
+
+      /**
+       * return the currently active lexer condition state; when an index
+       * argument is provided it produces the N-th previous condition state,
+       * if available
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      topState: function lexer_topState(n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+
+        if (n >= 0) {
+          return this.conditionStack[n];
+        } else {
+          return 'INITIAL';
+        }
+      },
+
+      /**
+       * (internal) determine the lexer rule set which is active for the
+       * currently active lexer condition state
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      _currentRules: function lexer__currentRules() {
+        if (this.conditionStack.length &&
+            this.conditionStack[this.conditionStack.length - 1]) {
+          return this
+              .conditions[this.conditionStack[this.conditionStack.length - 1]];
+        } else {
+          return this.conditions['INITIAL'];
+        }
+      },
+
+      /**
+       * return the number of states currently on the stack
+       *
+       * @public
+       * @this {RegExpLexer}
+       */
+      stateStackSize: function lexer_stateStackSize() {
+        return this.conditionStack.length;
+      },
+
+      options: {trackPosition: true, caseInsensitive: true},
+
+      JisonLexerError,
+
+      performAction: function lexer__performAction(yy, yyrulenumber, YY_START) {
+        var yY = this;
+        var YYSTATE = YY_START;
+
+        switch (yyrulenumber) {
+          case 21:
+            break;
+
+          default:
+            return this.simpleCaseActionClusters[yyrulenumber];
+        }
+      },
+
+      simpleCaseActionClusters: {
+        /*! Conditions:: INITIAL */
+        /*! Rule::       [<][^\n]+ */
+        0: 10,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       chrome[:][/][/][^\n]* */
+        1: 9,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       "[^"]*" */
+        2: 18,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       \n */
+        3: 4,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       [0-9]+ */
+        4: 12,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       [(] */
+        5: 11,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       [)] */
+        6: 14,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       [,] */
+        7: 13,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       expect */
+        8: 8,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       focus */
+        9: 15,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       load[ ]page */
+        10: 3,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       next */
+        11: 5,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       on */
+        12: 16,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       previous */
+        13: 6,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       select */
+        14: 7,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       button */
+        15: 17,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       slider */
+        16: 17,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       spinButton */
+        17: 17,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       textField */
+        18: 17,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       textFieldWithComboBox */
+        19: 17,
+
+        /*! Conditions:: INITIAL */
+        /*! Rule::       $ */
+        20: 1
+      },
+
+      rules: [
+        /*  0: */ /^(?:[<][^\n]+)/i,
+        /*  1: */ /^(?:chrome[:][/][/][^\n]*)/i,
+        /*  2: */ /^(?:"[^"]*")/i,
+        /*  3: */ /^(?:\n)/i,
+        /*  4: */ /^(?:\d+)/i,
+        /*  5: */ /^(?:[(])/i,
+        /*  6: */ /^(?:[)])/i,
+        /*  7: */ /^(?:[,])/i,
+        /*  8: */ /^(?:expect)/i,
+        /*  9: */ /^(?:focus)/i,
+        /* 10: */ /^(?:load[ ]page)/i,
+        /* 11: */ /^(?:next)/i,
+        /* 12: */ /^(?:on)/i,
+        /* 13: */ /^(?:previous)/i,
+        /* 14: */ /^(?:select)/i,
+        /* 15: */ /^(?:button)/i,
+        /* 16: */ /^(?:slider)/i,
+        /* 17: */ /^(?:spinButton)/i,
+        /* 18: */ /^(?:textField)/i,
+        /* 19: */ /^(?:textFieldWithComboBox)/i,
+        /* 20: */ /^(?:$)/i,
+        /* 21: */ /^(?:\s)/i
+      ],
+
+      conditions: {
+        'INITIAL': {
+          rules: [
+            0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+            11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
+          ],
+
+          inclusive: true
+        }
+      }
+    };
+
+    return lexer;
+  }();
+  parser.lexer = lexer;
+
+  let page, point, objectToMatch;
+
+  let indent = '';
+  const increaseIndent = () => indent = indent + '  ';
+  const decreaseIndent = () => indent = indent.substring(2);
+
+  let buffer = '';
+  const addToBuffer = (text) => buffer += indent + text + '\n';
+  const flushBuffer = () => {
+    const result = buffer;
+    buffer = '';
+    return result;
+  };
+
+  function addAllStatements(statements) {
+    if (statements.find(statement => !statement.output)) {
+      throw new Error('Compiler error: statement not converted to Javascript');
+    }
+    statements.forEach(statement => addToBuffer(statement.output));
+    return flushBuffer();
+  }
+
+  // Initialize test before anything else.
+  const initTest = (page = `''`) => {
+    addToBuffer(`TEST_F('SwitchAccessSAATLiteTest', 'Demo', function() {`);
+    increaseIndent();
+    addToBuffer(
+        `this.runWithLoadedTree(${page.output}, async (rootWebArea) => {`);
+    increaseIndent();
+    addToBuffer('TestUtility.startFocusInside(rootWebArea);');
+    return flushBuffer();
+  };
+
+  const finishTest = (opt_url) => {
+    decreaseIndent();
+    if (opt_url) {
+      addToBuffer(`}, {url: ${opt_url.output}});`);
+    } else {
+      addToBuffer(`});`);
+    }
+    decreaseIndent();
+    addToBuffer(`});`);
+
+    return flushBuffer();
+  };
+
+  function Parser() {
+    this.yy = {};
+  }
+  Parser.prototype = parser;
+  parser.Parser = Parser;
+
+  return new Parser();
+})();
+
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+  exports.parser = parser;
+  exports.Parser = parser.Parser;
+  exports.parse = function() {
+    return parser.parse.apply(parser, arguments);
+  };
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js
new file mode 100644
index 0000000..efb57d4
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js
@@ -0,0 +1,27 @@
+// 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.
+
+GEN_INCLUDE(['../../switch_access_e2e_test_base.js', '../../test_utility.js']);
+
+/** Test fixture for the SAATLite generated tests. */
+SwitchAccessSAATLiteTest = class extends SwitchAccessE2ETest {
+  /** @override */
+  setUp() {
+    const runTest = this.deferRunTest(WhenTestDone.EXPECT);
+    (async () => {
+      await TestUtility.setup();
+      runTest();
+    })();
+  }
+};
+
+TEST_F('SwitchAccessSAATLiteTest', 'Demo', function() {
+  this.runWithLoadedTree('<button>Hi</button>', async (rootWebArea) => {
+    TestUtility.startFocusInside(rootWebArea);
+    TestUtility.pressNextSwitch();
+    TestUtility.pressPreviousSwitch();
+    TestUtility.pressSelectSwitch();
+    await TestUtility.expectFocusOn({role: 'button'});
+  });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/tests/demo.saatl b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/tests/demo.saatl
new file mode 100644
index 0000000..2d1945e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/tests/demo.saatl
@@ -0,0 +1,5 @@
+load page <button>Hi</button>
+next
+previous
+select
+expect focus on button
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
index 6287d69..b855f5b05 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
@@ -233,7 +233,9 @@
     /** @private */
     dictationLocaleMenuSubtitle_: {
       type: String,
-      value: '',
+      computed: 'computeDictationLocaleSubtitle_(' +
+          'dictationLocaleOptions_, ' +
+          'prefs.settings.a11y.dictation_locale.value)',
     },
 
     /** @private */
@@ -672,14 +674,15 @@
                     localeInfo.value === currentLocale,
               };
             });
-    this.updateDictationLocaleSubtitle_();
   },
 
   /**
-   * Updates the Dictation locale subtitle.
+   * Calculates the Dictation locale subtitle based on the current
+   * locale from prefs and the offline availability of that locale.
+   * @return {string}
    * @private
    */
-  updateDictationLocaleSubtitle_() {
+  computeDictationLocaleSubtitle_() {
     const currentLocale =
         this.get('prefs.settings.a11y.dictation_locale.value');
     const locale = this.dictationLocaleOptions_.find(
@@ -687,7 +690,7 @@
     if (!locale) {
       return '';
     }
-    this.dictationLocaleMenuSubtitle_ = this.i18n(
+    return this.i18n(
         locale.offline ? 'dictationLocaleSubLabelOffline' :
                          'dictationLocaleSubLabelNetwork',
         locale.name);
@@ -703,6 +706,5 @@
   /** @private */
   onChangeDictationLocalesDialogClosed_() {
     this.showDictationLocaleMenu_ = false;
-    this.updateDictationLocaleSubtitle_();
   },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.html b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.html
index ccff110..e3d897b8 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.html
@@ -37,19 +37,30 @@
 </div>
 <div class="device-lists-separator"></div>
 <div id="container">
-  <!-- TODO(crbug.com/1010321): Populate with real data. -->
-  <div class="settings-box-text">
-    $i18n{bluetoothDeviceListCurrentlyConnected}
-  </div>
-  <div class="device-list">
-    <os-settings-paired-bluetooth-list>
-    </os-settings-paired-bluetooth-list>
-  </div>
-  <div class="settings-box-text">
-    $i18n{bluetoothDeviceListPreviouslyConnected}
-  </div>
-  <div class="device-list">
-    <os-settings-paired-bluetooth-list>
-    </os-settings-paired-bluetooth-list>
-  </div>
+  <template is="dom-if"
+      if="[[shouldShowDeviceList_(connectedDevices_,
+          connectedDevices_.length)]]" restamp>
+    <div class="settings-box-text">
+      $i18n{bluetoothDeviceListCurrentlyConnected}
+    </div>
+    <div class="device-list">
+      <os-settings-paired-bluetooth-list
+          id="connectedDeviceList"
+          devices="[[connectedDevices_]]">
+      </os-settings-paired-bluetooth-list>
+    </div>
+  </template>
+  <template is="dom-if"
+      if="[[shouldShowDeviceList_(unconnectedDevices_,
+          unconnectedDevices_.length)]]" restamp>
+    <div class="settings-box-text">
+      $i18n{bluetoothDeviceListPreviouslyConnected}
+    </div>
+    <div class="device-list">
+      <os-settings-paired-bluetooth-list
+          id="unconnectedDeviceList"
+          devices="[[unconnectedDevices_]]">
+      </os-settings-paired-bluetooth-list>
+    </div>
+  </template>
 </div>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js
index b30e191d..af61583 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js
@@ -55,6 +55,22 @@
         type: Boolean,
         observer: 'onBluetoothToggleChanged_',
       },
+
+      /**
+       * @private {!Array<!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties>}
+       */
+      connectedDevices_: {
+        type: Array,
+        value: [],
+      },
+
+      /**
+       * @private {!Array<!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties>}
+       */
+      unconnectedDevices_: {
+        type: Array,
+        value: [],
+      }
     };
   }
 
@@ -67,6 +83,13 @@
             mojom.BluetoothSystemState.kEnabled ||
         this.systemProperties.systemState ===
             mojom.BluetoothSystemState.kEnabling;
+
+    this.connectedDevices_ = this.systemProperties.pairedDevices.filter(
+        device => device.deviceProperties.connectionState !==
+            mojom.DeviceConnectionState.kNotConnected);
+    this.unconnectedDevices_ = this.systemProperties.pairedDevices.filter(
+        device => device.deviceProperties.connectionState ===
+            mojom.DeviceConnectionState.kNotConnected);
   }
 
   /** @private */
@@ -95,6 +118,16 @@
   getOnOffString_(isBluetoothToggleOn, onString, offString) {
     return isBluetoothToggleOn ? onString : offString;
   }
+
+  /**
+   * @param {!Array<!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties>}
+   *     devices
+   * @return boolean
+   * @private
+   */
+  shouldShowDeviceList_(devices) {
+    return devices.length > 0;
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.js
index 3a1b55e..ee39097 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.js
@@ -36,8 +36,7 @@
   static get properties() {
     return {
       /**
-       * TODO(crbug.com/1010321): Use actual Device objects.
-       * @private {Array<Object>}
+       * @private {!Array<!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties>}
        */
       devices: {
         type: Array,
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.cc b/chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.cc
index 4427a3f..9a2c663 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.cc
@@ -6,13 +6,13 @@
 
 #include "base/check.h"
 #include "base/memory/singleton.h"
-#include "chrome/browser/chromeos/service_sandbox_type.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
 #include "chrome/common/extensions/extension_constants.h"
+#include "chromeos/services/tts/public/mojom/tts_service.mojom.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ui/android/multiwindow/BUILD.gn b/chrome/browser/ui/android/multiwindow/BUILD.gn
index a3f8097b..2afd176 100644
--- a/chrome/browser/ui/android/multiwindow/BUILD.gn
+++ b/chrome/browser/ui/android/multiwindow/BUILD.gn
@@ -36,6 +36,7 @@
 android_resources("java_resources") {
   sources = [
     "java/res/drawable/circle_green.xml",
+    "java/res/layout/instance_switcher_cmd_item.xml",
     "java/res/layout/instance_switcher_dialog.xml",
     "java/res/layout/instance_switcher_item.xml",
     "java/res/layout/instance_switcher_list.xml",
diff --git a/chrome/browser/ui/android/multiwindow/java/res/layout/instance_switcher_cmd_item.xml b/chrome/browser/ui/android/multiwindow/java/res/layout/instance_switcher_cmd_item.xml
new file mode 100644
index 0000000..f0ea4407
--- /dev/null
+++ b/chrome/browser/ui/android/multiwindow/java/res/layout/instance_switcher_cmd_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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. -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/content"
+    style="@style/ListItemContainer" >
+
+    <ImageView
+        android:id="@+id/favicon"
+        android:src="@drawable/ic_add"
+        android:layout_toEndOf="@id/current"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_marginStart="48dp"
+        android:layout_marginEnd="8dp"
+        android:layout_marginVertical="8dp"
+        android:scaleType="fitCenter"
+        android:layout_gravity="center_vertical"
+        app:tint="@color/default_bg_color_blue"
+        android:importantForAccessibility="no" />
+
+    <FrameLayout
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toEndOf="@id/favicon"
+        android:layout_toStartOf="@id/end_button"
+        android:layout_alignParentEnd="true"
+        android:paddingEnd="@dimen/default_list_row_padding"
+        android:layout_gravity="center_vertical" >
+
+        <TextView
+            android:id="@+id/command"
+            android:text="@string/menu_new_window"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
+        <TextView
+            android:id="@+id/max_info"
+            android:text="@string/max_number_of_windows"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
+    </FrameLayout>
+</RelativeLayout>
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinator.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinator.java
index eb90760..074898c9 100644
--- a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinator.java
+++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinator.java
@@ -37,6 +37,7 @@
 
 /**
  * Coordinator to construct the instance switcher dialog.
+ * TODO: Resolve various inconsistencies that can be caused by Ui from multiple instances.
  */
 public class InstanceSwitcherCoordinator {
     /**
@@ -52,6 +53,7 @@
     private final Context mContext;
     private final Callback<InstanceInfo> mOpenCallback;
     private final Callback<InstanceInfo> mCloseCallback;
+    private final Runnable mNewWindowAction;
     private final ModalDialogManager mModalDialogManager;
 
     private final ModelList mModelList = new ModelList();
@@ -62,6 +64,8 @@
     private PropertyModel mConfirmDialog;
     private InstanceInfo mItemToDelete;
     private boolean mIsShowingConfirmationMessage;
+    private PropertyModel mNewWindowModel;
+    private boolean mNewWindowEnabled;
 
     /**
      * Show instance switcher modal dialog UI.
@@ -70,24 +74,28 @@
      * @param iconBridge An object that fetches favicons from local DB.
      * @param openCallback Callback to invoke to open a chosen instance.
      * @param closeCallback Callback to invoke to close a chosen instance.
+     * @param newWindowAction Runnable to invoke to open a new window.
+     * @param newWindowEnabled True if the "New window" command needs to be enabled.
      * @param instanceInfo List of {@link InstanceInfo} for available Chrome instances.
      */
     public static void showDialog(Context context, ModalDialogManager modalDialogManager,
             LargeIconBridge iconBridge, Callback<InstanceInfo> openCallback,
-            Callback<InstanceInfo> closeCallback, List<InstanceInfo> instanceInfo) {
-        new InstanceSwitcherCoordinator(
-                context, modalDialogManager, iconBridge, openCallback, closeCallback)
-                .showDialog(instanceInfo);
+            Callback<InstanceInfo> closeCallback, Runnable newWindowAction,
+            boolean newWindowEnabled, List<InstanceInfo> instanceInfo) {
+        new InstanceSwitcherCoordinator(context, modalDialogManager, iconBridge, openCallback,
+                closeCallback, newWindowAction)
+                .showDialog(instanceInfo, newWindowEnabled);
     }
 
     private InstanceSwitcherCoordinator(Context context, ModalDialogManager modalDialogManager,
             LargeIconBridge iconBridge, Callback<InstanceInfo> openCallback,
-            Callback<InstanceInfo> closeCallback) {
+            Callback<InstanceInfo> closeCallback, Runnable newWindowAction) {
         mContext = context;
         mModalDialogManager = modalDialogManager;
         mOpenCallback = openCallback;
         mCloseCallback = closeCallback;
         mUiUtils = new UiUtils(mContext, iconBridge);
+        mNewWindowAction = newWindowAction;
 
         ModelListAdapter adapter = new ModelListAdapter(mModelList);
         // TODO: Extend modern_list_item_view.xml to replace instance_switcher_item.xml
@@ -95,17 +103,24 @@
                 parentView
                 -> LayoutInflater.from(mContext).inflate(R.layout.instance_switcher_item, null),
                 InstanceSwitcherItemViewBinder::bind);
+        adapter.registerType(EntryType.COMMAND,
+                parentView
+                -> LayoutInflater.from(mContext).inflate(R.layout.instance_switcher_cmd_item, null),
+                InstanceSwitcherItemViewBinder::bind);
         mDialogView = LayoutInflater.from(context).inflate(R.layout.instance_switcher_dialog, null);
         ListView listView = (ListView) mDialogView.findViewById(R.id.list_view);
         listView.setAdapter(adapter);
     }
 
-    private void showDialog(List<InstanceInfo> items) {
+    private void showDialog(List<InstanceInfo> items, boolean newWindowEnabled) {
         for (int i = 0; i < items.size(); ++i) {
             PropertyModel itemModel = generateListItem(items.get(i));
             mModelList.add(new ModelListAdapter.ListItem(EntryType.INSTANCE, itemModel));
         }
-        // TODO: Add "+ New Window" menu item at the bottom of the list.
+        mNewWindowModel = new PropertyModel(InstanceSwitcherItemProperties.ALL_KEYS);
+        enableNewWindowCommand(newWindowEnabled);
+        mModelList.add(new ModelListAdapter.ListItem(EntryType.COMMAND, mNewWindowModel));
+
         mDialog = createDialog(mDialogView, mModelList, items);
         mModalDialogManager.showDialog(mDialog, ModalDialogType.APP);
     }
@@ -165,6 +180,21 @@
         return model;
     }
 
+    private void enableNewWindowCommand(boolean enabled) {
+        if (mNewWindowEnabled && enabled) return;
+        mNewWindowModel.set(InstanceSwitcherItemProperties.ENABLE_COMMAND, enabled);
+        if (enabled) {
+            mNewWindowModel.set(
+                    InstanceSwitcherItemProperties.CLICK_LISTENER, this::newWindowAction);
+        }
+        mNewWindowEnabled = enabled;
+    }
+
+    private void newWindowAction(View view) {
+        dismissDialog(DialogDismissalCause.ACTION_ON_CONTENT);
+        mNewWindowAction.run();
+    }
+
     private void buildMoreMenu(PropertyModel.Builder builder, InstanceInfo item) {
         ModelList moreMenu = new ModelList();
         moreMenu.add(buildMenuListItem(R.string.instance_switcher_close_window, 0, 0));
@@ -209,6 +239,9 @@
             }
         }
         mCloseCallback.onResult(item);
+
+        // Removing an instance enables the new window item.
+        enableNewWindowCommand(true);
     }
 
     private void showConfirmationMessage(InstanceInfo item) {
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinatorTest.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinatorTest.java
index 2e27db8..a256ad43 100644
--- a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinatorTest.java
+++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherCoordinatorTest.java
@@ -71,9 +71,28 @@
         Callback<InstanceInfo> openCallback = (item) -> itemClickCallbackHelper.notifyCalled();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             InstanceSwitcherCoordinator.showDialog(getActivity(), mModalDialogManager, mIconBridge,
-                    openCallback, null, Arrays.asList(instances));
+                    openCallback, null, null, false, Arrays.asList(instances));
         });
         onData(anything()).atPosition(1).perform(click());
         itemClickCallbackHelper.waitForCallback(itemClickCount);
     }
+
+    @Test
+    @SmallTest
+    public void testInstanceSwitcherCoordinator_newWindow() throws Exception {
+        InstanceInfo[] instances = new InstanceInfo[] {
+                new InstanceInfo(0, 57, InstanceInfo.Type.CURRENT, "url0", "title0", 1, 0, false),
+                new InstanceInfo(1, 58, InstanceInfo.Type.OTHER, "ur11", "title1", 2, 0, false),
+                new InstanceInfo(2, 59, InstanceInfo.Type.OTHER, "url2", "title2", 1, 1, false)};
+        final CallbackHelper itemClickCallbackHelper = new CallbackHelper();
+        final int itemClickCount = itemClickCallbackHelper.getCallCount();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            InstanceSwitcherCoordinator.showDialog(getActivity(), mModalDialogManager, mIconBridge,
+                    null, null, itemClickCallbackHelper::notifyCalled, true,
+                    Arrays.asList(instances));
+        });
+        // 0 ~ 2: instances. 3: 'new window' command.
+        onData(anything()).atPosition(3).perform(click());
+        itemClickCallbackHelper.waitForCallback(itemClickCount);
+    }
 }
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemProperties.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemProperties.java
index 798c97ff..4b6d2901 100644
--- a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemProperties.java
+++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemProperties.java
@@ -18,6 +18,9 @@
     public static final PropertyModel.WritableBooleanPropertyKey CURRENT =
             new PropertyModel.WritableBooleanPropertyKey();
 
+    public static final PropertyModel.WritableBooleanPropertyKey ENABLE_COMMAND =
+            new PropertyModel.WritableBooleanPropertyKey();
+
     public static final PropertyModel.WritableObjectPropertyKey<Drawable> FAVICON =
             new PropertyModel.WritableObjectPropertyKey<>();
 
@@ -38,5 +41,5 @@
             new PropertyModel.WritableObjectPropertyKey<>();
 
     public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {
-            CURRENT, FAVICON, TITLE, DESC, INSTANCE_ID, CLICK_LISTENER, MORE_MENU};
+            CURRENT, ENABLE_COMMAND, FAVICON, TITLE, DESC, INSTANCE_ID, CLICK_LISTENER, MORE_MENU};
 }
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemViewBinder.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemViewBinder.java
index 6d96d27..6ac8b62 100644
--- a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemViewBinder.java
+++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/InstanceSwitcherItemViewBinder.java
@@ -20,12 +20,22 @@
                     .setImageDrawable(model.get(InstanceSwitcherItemProperties.FAVICON));
 
         } else if (InstanceSwitcherItemProperties.TITLE == propertyKey) {
-            ((TextView) view.findViewById(R.id.title))
-                    .setText(model.get(InstanceSwitcherItemProperties.TITLE));
+            TextView titleView = (TextView) view.findViewById(R.id.title);
+            String text = model.get(InstanceSwitcherItemProperties.TITLE);
+            if (text != null) {
+                titleView.setText(text);
+            } else {
+                titleView.setVisibility(View.GONE);
+            }
 
         } else if (InstanceSwitcherItemProperties.DESC == propertyKey) {
-            ((TextView) view.findViewById(R.id.desc))
-                    .setText(model.get(InstanceSwitcherItemProperties.DESC));
+            TextView descView = (TextView) view.findViewById(R.id.desc);
+            String text = model.get(InstanceSwitcherItemProperties.DESC);
+            if (text != null) {
+                descView.setText(text);
+            } else {
+                descView.setVisibility(View.GONE);
+            }
 
         } else if (InstanceSwitcherItemProperties.CURRENT == propertyKey) {
             boolean current = model.get(InstanceSwitcherItemProperties.CURRENT);
@@ -39,6 +49,15 @@
         } else if (InstanceSwitcherItemProperties.MORE_MENU == propertyKey) {
             ListMenuButtonDelegate delegate = model.get(InstanceSwitcherItemProperties.MORE_MENU);
             ((ListMenuButton) view.findViewById(R.id.more)).setDelegate(delegate);
+
+        } else if (InstanceSwitcherItemProperties.ENABLE_COMMAND == propertyKey) {
+            View command = view.findViewById(R.id.command);
+            View info = view.findViewById(R.id.max_info);
+            View favicon = view.findViewById(R.id.favicon);
+            boolean enabled = model.get(InstanceSwitcherItemProperties.ENABLE_COMMAND);
+            command.setVisibility(enabled ? View.VISIBLE : View.GONE);
+            favicon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+            info.setVisibility(enabled ? View.GONE : View.VISIBLE);
         }
     }
 }
diff --git a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
index dc99c4e..f7785db 100644
--- a/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/network_config_service.h"
 #include "chrome/browser/ash/assistant/assistant_util.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/service_sandbox_type.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ash/assistant/assistant_context_util.h"
@@ -21,6 +20,7 @@
 #include "chrome/browser/ui/ash/assistant/conversation_starters_client_impl.h"
 #include "chrome/browser/ui/ash/assistant/device_actions_delegate_impl.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
+#include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/audio_service.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
index c65eb290..58dc651 100644
--- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/ash/policy/dlp/dlp_content_manager.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/service_sandbox_type.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge.h b/chrome/browser/ui/cocoa/history_menu_bridge.h
index 1a81df9..9276d95 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge.h
+++ b/chrome/browser/ui/cocoa/history_menu_bridge.h
@@ -116,14 +116,11 @@
   enum Tags {
     kRecentlyClosedSeparator = 400,  // Item before recently closed section.
     kRecentlyClosedTitle = 401,      // Title of recently closed section.
-    kRecentlyClosed = 420,     // Used for items in the recently closed section.
-    kVisitedSeparator = 440,   // Separator before visited section.
-    kVisitedTitle = 441,       // Title of the visited section.
-    kVisited = 460,            // Used for all entries in the visited section.
-    kShowFullSeparator = 480,  // Separator after the visited section.
-    kIncognitoDisclaimerSeparator =
-        500,  // Separator before Incognito disclaimer text.
-    kIncognitoDisclaimerLabel = 501  // Label for Incognito disclaimer text.
+    kRecentlyClosed = 420,    // Used for items in the recently closed section.
+    kVisitedSeparator = 440,  // Separator before visited section.
+    kVisitedTitle = 441,      // Title of the visited section.
+    kVisited = 460,           // Used for all entries in the visited section.
+    kShowFullSeparator = 480  // Separator after the visited section.
   };
 
   explicit HistoryMenuBridge(Profile* profile);
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge.mm b/chrome/browser/ui/cocoa/history_menu_bridge.mm
index 26e3b23..ef6b867 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/history_menu_bridge.mm
@@ -73,10 +73,6 @@
   NSMenuItem* full_history_item = [HistoryMenu() itemWithTag:IDC_SHOW_HISTORY];
   [full_history_item
       setImage:rb.GetNativeImageNamed(IDR_HISTORY_FAVICON).ToNSImage()];
-  NSMenuItem* incognito_disclaimer_item =
-      [HistoryMenu() itemWithTag:kIncognitoDisclaimerLabel];
-  [incognito_disclaimer_item
-      setImage:rb.GetNativeImageNamed(IDR_INFO_FAVICON).ToNSImage()];
 
   // Set the visibility of menu items according to profile type.
   // "Recently Visited", "Recently Closed" and "Show Full History" sections
@@ -577,8 +573,7 @@
 bool HistoryMenuBridge::ShouldMenuItemBeVisible(NSMenuItem* item) {
   if (!base::FeatureList::IsEnabled(
           features::kUpdateHistoryEntryPointsInIncognito)) {
-    return [item tag] != kIncognitoDisclaimerLabel &&
-           [item tag] != kIncognitoDisclaimerSeparator;
+    return true;
   }
 
   int tag = [item tag];
@@ -596,10 +591,6 @@
     case kShowFullSeparator:
     case IDC_SHOW_HISTORY:
       return !profile_->IsOffTheRecord();
-    // The incognito profile specific menu items
-    case kIncognitoDisclaimerSeparator:
-    case kIncognitoDisclaimerLabel:
-      return profile_->IsOffTheRecord();
   }
 
   // When a new menu item is introduced, it should be added to one of the cases
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm
index 008dd9a2..ea8b695 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/history_menu_bridge_unittest.mm
@@ -209,18 +209,6 @@
     item.get().tag = regular_visible_items[i];
     EXPECT_EQ(!is_incognito, test->ShouldMenuItemBeVisible(item));
   }
-
-  // Check visibilty of items belong to incognito mode. They should be visible
-  // for incognito mode, not for regular mode.
-  NSInteger incognito_visible_items[] = {
-      HistoryMenuBridge::kIncognitoDisclaimerSeparator,
-      HistoryMenuBridge::kIncognitoDisclaimerLabel};
-  for (size_t i = 0; i < base::size(incognito_visible_items); i++) {
-    // Create a fake item with tag.
-    base::scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc] init]);
-    item.get().tag = incognito_visible_items[i];
-    EXPECT_EQ(is_incognito, test->ShouldMenuItemBeVisible(item));
-  }
 }
 
 // Edge case test for clearing until the end of a menu.
diff --git a/chrome/browser/ui/cocoa/main_menu_builder.mm b/chrome/browser/ui/cocoa/main_menu_builder.mm
index 7270fa1..ed29003a 100644
--- a/chrome/browser/ui/cocoa/main_menu_builder.mm
+++ b/chrome/browser/ui/cocoa/main_menu_builder.mm
@@ -326,15 +326,6 @@
               Item(IDS_HISTORY_SHOWFULLHISTORY_LINK)
                   .command_id(IDC_SHOW_HISTORY)
                   .remove_if(is_pwa),
-              Item()
-                  .tag(HistoryMenuBridge::kIncognitoDisclaimerSeparator)
-                  .is_separator()
-                  .set_hidden(true)
-                  .remove_if(is_pwa),
-              Item(IDS_HISTORY_INCOGNITO_DISCLAIMER_MAC)
-                  .tag(HistoryMenuBridge::kIncognitoDisclaimerLabel)
-                  .set_hidden(true)
-                  .remove_if(is_pwa),
           })
           .Build();
   return item;
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc
index 7d48a90..cab5773a 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.cc
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -98,6 +98,7 @@
 }
 
 void CoreTabHelper::SearchWithLensInNewTab(gfx::Image image,
+                                           const gfx::Size& image_original_size,
                                            lens::EntryPoint entry_point) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
@@ -122,7 +123,7 @@
 
   search_args.image_thumbnail_content.assign(image_bytes_begin,
                                              image_bytes_end);
-  search_args.image_original_size = image.Size();
+  search_args.image_original_size = image_original_size;
   search_args.additional_query_params =
       lens::GetQueryParameterFromEntryPoint(entry_point);
 
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.h b/chrome/browser/ui/tab_contents/core_tab_helper.h
index 64d53639..973dee97 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.h
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.h
@@ -41,8 +41,11 @@
                               lens::EntryPoint entry_point);
 
   // Open the Lens experience for an image. Used for sending the bitmap selected
-  // via Lens Region Search.
-  void SearchWithLensInNewTab(gfx::Image image, lens::EntryPoint entry_point);
+  // via Lens Region Search. |image_original_size| is specified in case of
+  // resizing that happens prior to passing the image to CoreTabHelper.
+  void SearchWithLensInNewTab(gfx::Image image,
+                              const gfx::Size& image_original_size,
+                              lens::EntryPoint entry_point);
 
   // Perform an image search for the image that triggered the context menu.  The
   // |src_url| is passed to the search request and is not used directly to fetch
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index d3b85d6..e6cb0d9 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -144,19 +144,17 @@
 
       // The label of the overlay will now show the error in red.
       auto error_label = std::make_unique<views::Label>(error_message);
-      const SkColor warning_text_color = views::style::GetColor(
-          *error_label, ChromeTextContext::CONTEXT_DIALOG_BODY_TEXT_SMALL,
-          STYLE_RED);
-      error_label->SetEnabledColor(warning_text_color);
+      views::SetCascadingNativeThemeColor(
+          error_label.get(), views::kCascadingLabelEnabledColor,
+          ui::NativeTheme::kColorId_AlertSeverityHigh);
       error_label->SetMultiLine(true);
 
       // Replace the throbber with a warning icon. Since this is a permanent
       // error we do not intend to return to a previous state.
-      auto error_icon = std::make_unique<views::ImageView>();
-      error_icon->SetImage(gfx::CreateVectorIcon(
-          kBrowserToolsErrorIcon,
-          GetNativeTheme()->GetSystemColor(
-              ui::NativeTheme::kColorId_AlertSeverityHigh)));
+      auto error_icon =
+          std::make_unique<views::ImageView>(ui::ImageModel::FromVectorIcon(
+              kBrowserToolsErrorIcon,
+              ui::NativeTheme::kColorId_AlertSeverityHigh));
 
       layout->StartRow(1.0, 0);
       layout->AddView(std::move(error_icon));
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
index 120761c..3780c74 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
@@ -16,7 +16,11 @@
 #include "chrome/browser/ui/views/media_router/cast_dialog_sink_button.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/media_message_center/media_notification_item.h"
+#include "components/media_router/browser/media_router_metrics.h"
+#include "components/media_router/common/mojom/media_route_provider_id.mojom-shared.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "media/audio/audio_device_description.h"
 #include "media/base/media_switches.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
@@ -28,6 +32,8 @@
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/layout/box_layout.h"
 
+using media_router::mojom::MediaRouteProviderId;
+
 namespace {
 
 // Constants for the MediaNotificationDeviceSelectorView
@@ -41,6 +47,39 @@
 // devices.
 const int kAudioDevicesCountHistogramMax = 30;
 
+media_router::MediaRouterDialogOpenOrigin ConvertToOrigin(
+    GlobalMediaControlsEntryPoint entry_point) {
+  switch (entry_point) {
+    case GlobalMediaControlsEntryPoint::kPresentation:
+      return media_router::MediaRouterDialogOpenOrigin::PAGE;
+    case GlobalMediaControlsEntryPoint::kSystemTray:
+      return media_router::MediaRouterDialogOpenOrigin::SYSTEM_TRAY;
+    case GlobalMediaControlsEntryPoint::kToolbarIcon:
+      return media_router::MediaRouterDialogOpenOrigin::TOOLBAR;
+  }
+}
+
+void RecordCastDeviceCountMetrics(GlobalMediaControlsEntryPoint entry_point,
+                                  std::vector<CastDeviceEntryView*> entries) {
+  media_router::MediaRouterMetrics::RecordDeviceCount(entries.size());
+
+  std::map<MediaRouteProviderId, std::map<bool, int>> counts = {
+      {MediaRouteProviderId::CAST, {{true, 0}, {false, 0}}},
+      {MediaRouteProviderId::DIAL, {{true, 0}, {false, 0}}},
+      {MediaRouteProviderId::WIRED_DISPLAY, {{true, 0}, {false, 0}}}};
+  for (const CastDeviceEntryView* entry : entries) {
+    counts.at(entry->sink().provider).at(entry->GetEnabled())++;
+  }
+  for (auto provider : {MediaRouteProviderId::CAST, MediaRouteProviderId::DIAL,
+                        MediaRouteProviderId::WIRED_DISPLAY}) {
+    for (bool is_available : {true, false}) {
+      int count = counts.at(provider).at(is_available);
+      media_router::MediaRouterMetrics::RecordGmcDeviceCount(
+          ConvertToOrigin(entry_point), provider, is_available, count);
+    }
+  }
+}
+
 class ExpandDeviceSelectorButton : public IconLabelBubbleView {
  public:
   explicit ExpandDeviceSelectorButton(IconLabelBubbleView::Delegate* delegate);
@@ -289,6 +328,7 @@
         device_entry_views_container_->children().size(),
         kAudioDevicesCountHistogramMax);
     base::UmaHistogramBoolean(kDeviceSelectorOpenedHistogramName, true);
+    RecordCastDeviceCountAfterDelay();
     have_devices_been_shown_ = true;
   }
 
@@ -515,6 +555,26 @@
       action);
 }
 
+void MediaNotificationDeviceSelectorView::RecordCastDeviceCountAfterDelay() {
+  content::GetUIThreadTaskRunner({})->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &MediaNotificationDeviceSelectorView::RecordCastDeviceCount,
+          weak_ptr_factory_.GetWeakPtr()),
+      media_router::MediaRouterMetrics::kDeviceCountMetricDelay);
+}
+
+void MediaNotificationDeviceSelectorView::RecordCastDeviceCount() {
+  std::vector<CastDeviceEntryView*> entries;
+  for (views::View* view : device_entry_views_container_->children()) {
+    DeviceEntryUI* entry = GetDeviceEntryUI(view);
+    if (entry->GetType() == DeviceEntryUIType::kCast) {
+      entries.push_back(static_cast<CastDeviceEntryView*>(entry));
+    }
+  }
+  RecordCastDeviceCountMetrics(entry_point_, entries);
+}
+
 void MediaNotificationDeviceSelectorView::RegisterAudioDeviceCallbacks() {
   // Get a list of the connected audio output devices.
   audio_device_subscription_ =
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h
index fb2bfd0..80d27d7c 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h
@@ -19,6 +19,8 @@
 class ExpandDeviceSelectorButton;
 const char kAudioDevicesCountHistogramName[] =
     "Media.GlobalMediaControls.NumberOfAvailableAudioDevices";
+const char kCastDeviceCountHistogramName[] =
+    "Media.GlobalMediaControls.CastDeviceCount";
 const char kDeviceSelectorAvailableHistogramName[] =
     "Media.GlobalMediaControls.DeviceSelectorAvailable";
 const char kDeviceSelectorOpenedHistogramName[] =
@@ -115,6 +117,8 @@
   void DoStartCastSession(const media_router::UIMediaSink& sink);
   void RecordStartCastingMetrics();
   void RecordStopCastingMetrics();
+  void RecordCastDeviceCountAfterDelay();
+  void RecordCastDeviceCount();
   DeviceEntryUI* GetDeviceEntryUI(views::View* view) const;
   void RegisterAudioDeviceCallbacks();
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc b/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc
index 197d9902..ebd2727 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_metrics.cc
@@ -6,12 +6,16 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/media_router/ui_media_sink.h"
 #include "chrome/common/pref_names.h"
+#include "components/media_router/common/mojom/media_route_provider_id.mojom-shared.h"
 #include "components/media_router/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 
 namespace media_router {
 
+using mojom::MediaRouteProviderId;
+
 namespace {
 
 DialogActivationLocationAndCastMode GetActivationLocationAndCastMode(
@@ -91,6 +95,7 @@
     // |OVERFLOW_MENU| refers to extension icons hidden in the app menu. That
     // mode is no longer available for the Cast toolbar icon.
     case MediaRouterDialogOpenOrigin::OVERFLOW_MENU:
+    case MediaRouterDialogOpenOrigin::SYSTEM_TRAY:
     case MediaRouterDialogOpenOrigin::TOTAL_COUNT:
       break;
   }
@@ -164,8 +169,26 @@
   MaybeRecordFirstAction(MediaRouterUserAction::CLOSE);
 }
 
-void CastDialogMetrics::OnRecordSinkCount(int sink_count) {
-  media_router::MediaRouterMetrics::RecordDeviceCount(sink_count);
+void CastDialogMetrics::OnRecordSinkCount(
+    std::vector<const UIMediaSink*> sinks) {
+  media_router::MediaRouterMetrics::RecordDeviceCount(sinks.size());
+
+  std::map<MediaRouteProviderId, std::map<bool, int>> counts = {
+      {MediaRouteProviderId::CAST, {{true, 0}, {false, 0}}},
+      {MediaRouteProviderId::DIAL, {{true, 0}, {false, 0}}},
+      {MediaRouteProviderId::WIRED_DISPLAY, {{true, 0}, {false, 0}}}};
+  for (const UIMediaSink* sink : sinks) {
+    counts.at(sink->provider)
+        .at(sink->state != UIMediaSinkState::UNAVAILABLE)++;
+  }
+  for (auto provider : {MediaRouteProviderId::CAST, MediaRouteProviderId::DIAL,
+                        MediaRouteProviderId::WIRED_DISPLAY}) {
+    for (bool is_available : {true, false}) {
+      int count = counts.at(provider).at(is_available);
+      media_router::MediaRouterMetrics::RecordCastDialogDeviceCount(
+          activation_location_, provider, is_available, count);
+    }
+  }
 }
 
 void CastDialogMetrics::MaybeRecordFirstAction(MediaRouterUserAction action) {
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_metrics.h b/chrome/browser/ui/views/media_router/cast_dialog_metrics.h
index 7b71653..db12b5c 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_metrics.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_metrics.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/media_router/ui_media_sink.h"
 #include "components/media_router/browser/media_router_metrics.h"
 
 class Profile;
@@ -52,7 +53,7 @@
   void OnCloseDialog(const base::Time& close_time);
 
   // Records the number of sinks, which may be 0.
-  void OnRecordSinkCount(int sink_count);
+  void OnRecordSinkCount(std::vector<const UIMediaSink*> sinks);
 
  private:
   // Records the first user action if it hasn't already been recorded.
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc
index 0efacc7..76751c2 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_metrics_unittest.cc
@@ -6,8 +6,10 @@
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
+#include "chrome/browser/ui/media_router/ui_media_sink.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/media_router/common/mojom/media_route_provider_id.mojom-shared.h"
 #include "components/media_router/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
@@ -82,10 +84,13 @@
 }
 
 TEST_F(CastDialogMetricsTest, OnRecordSinkCount) {
-  constexpr int kSinkCount = 3;
-  metrics_.OnRecordSinkCount(kSinkCount);
+  UIMediaSink sink1{mojom::MediaRouteProviderId::CAST};
+  UIMediaSink sink2{mojom::MediaRouteProviderId::CAST};
+  UIMediaSink sink3{mojom::MediaRouteProviderId::DIAL};
+  std::vector<const UIMediaSink*> sinks{&sink1, &sink2, &sink3};
+  metrics_.OnRecordSinkCount(sinks);
   tester_.ExpectUniqueSample(MediaRouterMetrics::kHistogramUiDeviceCount,
-                             kSinkCount, 1);
+                             sinks.size(), 1);
 }
 
 TEST_F(CastDialogMetricsTest, RecordFirstAction) {
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index 13a5a9ea..6605453 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -441,11 +441,15 @@
       FROM_HERE,
       base::BindOnce(&CastDialogView::RecordSinkCount,
                      weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromSeconds(3));
+      MediaRouterMetrics::kDeviceCountMetricDelay);
 }
 
 void CastDialogView::RecordSinkCount() {
-  metrics_.OnRecordSinkCount(sink_buttons_.size());
+  std::vector<const UIMediaSink*> sinks;
+  for (CastDialogSinkButton* sink_button : sink_buttons_) {
+    sinks.push_back(&sink_button->sink());
+  }
+  metrics_.OnRecordSinkCount(sinks);
 }
 
 void CastDialogView::OnFilePickerClosed(const ui::SelectedFileInfo* file_info) {
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index cf6b517..defd4ccc 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -25,6 +25,7 @@
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/border.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/cascading_property.h"
 #include "ui/views/controls/button/button_controller.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/style/platform_style.h"
@@ -228,11 +229,8 @@
   if (!GetWidget())
     return;
 
-  const ui::NativeTheme* theme = GetNativeTheme();
   const SkColor icon_color =
-      active_ ? theme->GetSystemColor(
-                    ui::NativeTheme::kColorId_ProminentButtonColor)
-              : icon_color_;
+      active_ ? views::GetCascadingAccentColor(this) : icon_color_;
   const int icon_size = delegate_->GetPageActionIconSize();
   const gfx::ImageSkia image = gfx::CreateVectorIconWithBadge(
       GetVectorIcon(), icon_size, icon_color, GetVectorIconBadge());
diff --git a/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc b/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc
index 2b651379..ee5f0dd6 100644
--- a/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc
+++ b/chrome/browser/ui/views/user_education/feature_promo_bubble_view.cc
@@ -36,9 +36,11 @@
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/event_monitor.h"
-#include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/layout/layout_provider.h"
+#include "ui/views/layout/layout_types.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/view_class_properties.h"
@@ -218,29 +220,15 @@
       ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_BACKGROUND);
   const SkColor text_color = theme_provider->GetColor(
       ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_TEXT);
-  const int text_vertical_spacing = layout_provider->GetDistanceMetric(
-      views::DISTANCE_RELATED_CONTROL_VERTICAL);
-  const int button_vertical_spacing = layout_provider->GetDistanceMetric(
-      views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
 
-  auto box_layout = std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, kBubbleContentsInsets,
-      text_vertical_spacing);
-  box_layout->set_main_axis_alignment(
-      views::BoxLayout::MainAxisAlignment::kCenter);
-  box_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kStretch);
-  SetLayoutManager(std::move(box_layout));
+  // Add child views.
 
+  // Add progress indicator.
+  views::View* progress_indicator_container = nullptr;
   if (params.tutorial_progress_current) {
     DCHECK(params.tutorial_progress_max);
-    views::View* progress_indicator_container =
+    progress_indicator_container =
         AddChildView(std::make_unique<views::View>());
-    views::BoxLayout* const box_layout =
-        progress_indicator_container->SetLayoutManager(
-            std::make_unique<views::BoxLayout>(
-                views::BoxLayout::Orientation::kHorizontal));
-    box_layout->set_between_child_spacing(text_vertical_spacing);
 
     // TODO(crbug.com/1197208): surface progress information in a11y tree
 
@@ -254,8 +242,10 @@
     }
   }
 
+  // Add title label.
+  views::Label* title_label = nullptr;
   if (params.title_text.has_value()) {
-    auto* title_label = AddChildView(std::make_unique<views::Label>(
+    title_label = AddChildView(std::make_unique<views::Label>(
         std::move(*params.title_text),
         ChromeTextContext::CONTEXT_IPH_BUBBLE_TITLE));
     title_label->SetBackgroundColor(background_color);
@@ -266,6 +256,7 @@
       title_label->SetMultiLine(true);
   }
 
+  // Add body label.
   auto* body_label = AddChildView(
       std::make_unique<views::Label>(body_text, CONTEXT_IPH_BUBBLE_BODY));
   body_label->SetBackgroundColor(background_color);
@@ -273,17 +264,10 @@
   body_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   body_label->SetMultiLine(true);
 
+  // Add buttons.
+  views::View* button_container = nullptr;
   if (!params.buttons.empty()) {
-    auto* button_container = AddChildView(std::make_unique<views::View>());
-    auto* button_layout =
-        button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
-            views::BoxLayout::Orientation::kHorizontal));
-
-    button_layout->set_main_axis_alignment(
-        views::BoxLayout::MainAxisAlignment::kEnd);
-    button_container->SetProperty(
-        views::kMarginsKey, gfx::Insets(button_vertical_spacing, 0, 0, 0));
-
+    button_container = AddChildView(std::make_unique<views::View>());
     auto close_bubble_and_run_callback = [](FeaturePromoBubbleView* view,
                                             base::RepeatingClosure callback,
                                             const ui::Event& event) {
@@ -291,10 +275,6 @@
       callback.Run();
     };
 
-    const int button_spacing = layout_provider->GetDistanceMetric(
-        views::DISTANCE_RELATED_BUTTON_HORIZONTAL);
-
-    bool is_first_button = true;
     for (ButtonParams& button_params : params.buttons) {
       MdIPHBubbleButton* const button =
           button_container->AddChildView(std::make_unique<MdIPHBubbleButton>(
@@ -303,18 +283,72 @@
                                   std::move(button_params.callback)),
               std::move(button_params.text), button_params.has_border));
       buttons_.push_back(button);
-
       button->SetMinSize(gfx::Size(0, 0));
       button->SetCustomPadding(kBubbleButtonPadding);
-
-      if (!is_first_button) {
-        button->SetProperty(views::kMarginsKey,
-                            gfx::Insets(0, button_spacing, 0, 0));
-      }
-      is_first_button = false;
     }
   }
 
+  // Set up layouts. This is the default vertical spacing that is also used to
+  // separate progress indicators for symmetry.
+  // TODO(dfried): consider whether we could take font ascender and descender
+  // height and factor them into margin calculations.
+  const int default_spacing = layout_provider->GetDistanceMetric(
+      views::DISTANCE_RELATED_CONTROL_VERTICAL);
+
+  // Create primary layout (vertical).
+  SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical)
+      .SetMainAxisAlignment(views::LayoutAlignment::kCenter)
+      .SetInteriorMargin(kBubbleContentsInsets)
+      .SetCollapseMargins(true)
+      .SetDefault(views::kMarginsKey, gfx::Insets(0, 0, default_spacing, 0))
+      .SetIgnoreDefaultMainAxisMargins(true);
+
+  // Set up progress container layout.
+  if (progress_indicator_container) {
+    progress_indicator_container
+        ->SetLayoutManager(std::make_unique<views::FlexLayout>())
+        ->SetOrientation(views::LayoutOrientation::kHorizontal)
+        .SetDefault(views::kMarginsKey, gfx::Insets(0, default_spacing, 0, 0))
+        .SetIgnoreDefaultMainAxisMargins(true);
+  }
+
+  // Set label flex properties. This ensures that if the width of the bubble
+  // maxes out the text will shrink on the cross-axis and grow to multiple
+  // lines without getting cut off.
+  const views::FlexSpecification text_flex(
+      views::LayoutOrientation::kVertical,
+      views::MinimumFlexSizeRule::kPreferred,
+      views::MaximumFlexSizeRule::kPreferred,
+      /* adjust_height_for_width = */ true,
+      views::MinimumFlexSizeRule::kScaleToMinimum);
+  body_label->SetProperty(views::kFlexBehaviorKey, text_flex);
+  if (title_label)
+    title_label->SetProperty(views::kFlexBehaviorKey, text_flex);
+
+  // Set up button container layout.
+  if (button_container) {
+    // Add in the default spacing between bubble content and bottom/buttons.
+    button_container->SetProperty(
+        views::kMarginsKey,
+        gfx::Insets(layout_provider->GetDistanceMetric(
+                        views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL),
+                    0, 0, 0));
+
+    // Create button container internal layout.
+    button_container->SetLayoutManager(std::make_unique<views::FlexLayout>())
+        ->SetOrientation(views::LayoutOrientation::kHorizontal)
+        .SetMainAxisAlignment(views::LayoutAlignment::kEnd)
+        .SetDefault(views::kMarginsKey,
+                    gfx::Insets(0,
+                                layout_provider->GetDistanceMetric(
+                                    views::DISTANCE_RELATED_BUTTON_HORIZONTAL),
+                                0, 0))
+        .SetIgnoreDefaultMainAxisMargins(true);
+  }
+
+  // Set up the bubble itself.
+
   set_close_on_deactivate(!params.persist_on_blur);
 
   set_margins(gfx::Insets());
@@ -404,7 +438,8 @@
                      GetHeightForWidth(preferred_width_.value()));
   }
 
-  gfx::Size layout_manager_preferred_size = View::CalculatePreferredSize();
+  const gfx::Size layout_manager_preferred_size =
+      View::CalculatePreferredSize();
 
   // Wrap if the width is larger than |kBubbleMaxWidthDip|.
   if (layout_manager_preferred_size.width() > kBubbleMaxWidthDip) {
diff --git a/chrome/browser/ui/views/web_apps/README.md b/chrome/browser/ui/views/web_apps/README.md
index c5cbae5..976424d 100644
--- a/chrome/browser/ui/views/web_apps/README.md
+++ b/chrome/browser/ui/views/web_apps/README.md
@@ -1,56 +1,67 @@
 # dPWA Integration Tests
 
 The dPWA integration tests use a special framework. Each test is defined by a
-series of "testing actions", and test cases are read from a csv file.
-
-## Future work
-
-* Integrate with the tests generated by the integration testing script. (https://crbug.com/1215791)
-  * Create methods that match how the generated script tests use.
-  * Abandon the CSV file.
-  * Remove #if guards to have the generated tests compile & run.
-* Update documentation to cover the new way tests are disabled & delete the TestExpectations file.
+series of "testing actions", and test cases programmatically generated by a
+python script. The script will only yield tests for which every "action" is
+currently supported by the testing framework.
 
 ## Background
 
 See this [design doc](https://docs.google.com/document/d/e/2PACX-1vTFI0sXhZMvvg1B3sctYVUe64WbLVNzuXFUa6f3XyYTzKs2JnuFR8qKNyXYZsxE-rPPvsq__4ZCyrcS/pub) for background information, and the testing script [README](../../../../test/webapps/README.md) for how integration tests are generated.
 
-### Identifying and Diagnosing Failed Tests
+## Future work
 
-Every test will log a message that will give:
+* Fix how sync tests await web app quiescense. Currently, there is no waiting,
+  and tests will fail.
 
- * the failing test case
- * the line to add to the TestExpectations file to disable the test
- * the command line argument to specify to run the given test locally
+## Disabling a Test
 
-In addition to this, every testing action will be printed to console before it
-is executed, giving insight into where test failures are occurring.
+Tests can be disabled in the same manner that other integration/browser tests
+are disabled, using macros. See [On disabling
+tests](https://chromium.googlesource.com/chromium/src/+/main/docs/testing/on_disabling_tests.md)
+for more information.
 
-### Test Input Files
+## How to Contribute
 
-Test files live in //chrome/test/data/web_apps/:
- * web_app_integration_browsertest_cases.csv
- * web_app_integration_browsertest_cases_sync.csv
- * TestExpectations
+### Adding a Testing Action
 
-### Disabling a Test
+Adding support for a new testing action in the framework is as simple as adding
+a new method in `WebAppIntegrationBrowserTestBase`. In some cases, you may want
+to implement actions related to profile sync, and need to call into SyncTest
+helper methods. In this case, the meat of the action can be implemented in
+`TwoClientWebAppsIntegrationSyncTest`. From there, you implement a method of the
+same name in the base class, and have it call your sync action on the
+`TestDelegate` member (ie `delegate_->SwitchProfileClients()`).
 
-To disable a failing / crashing test, add an entry to the TestExpectations
-file mentioned above. The format is as follows:
-```
-crbug.com/id [ Platform ] [ Expectation ] list,of,actions,in,test
-```
+The list of testing actions are maintained in
+`chrome/test/webapps/data/framework_supported_actions.csv`. To add a new testing
+action, add the action into that csv. Use the emojis to indicate which platforms
+the action is supported on (at the time of your CL landing, rather than in the
+future).
 
-The list of supported platforms and expectations is maintained in the
-TestExpectations file. This test suite requires adding an entry
-per-platform that the test should be disabled on. Please create a bug for each
-test case added to this file.
+### Managing State
+
+After every state-change action, a state snapshot is constructed and stored as a
+member of WebAppIntegrationBrowserTestBase, `after_state_change_action_state_`.
+This is done in `ConstructStateSnapshot()`, called from
+`AfterStateChangeAction()`.  At the start of every state-change action, this
+state snapshot gets moved into the `before_state_change_action_state_` member,
+giving us the ability to compare the pre- and post-conditions of every
+state-change action.
+
+When adding actions, it may be useful to bolt onto this state snapshot in order
+to verify the results of state-change actions within state-check actions. To do
+this, simply add onto the relevant state objects, and update the objects `==`
+operator. Then, you need to make sure that your new state fields are updated in
+`ConstructStateSnapshot()`.
 
 ## How It Works
 
-This testing framework uses a script to generate a minimal set of
-test cases that produce the maximum amount of code coverage, and reading in that
-script output in these test implementations.
+This testing framework uses a [script](../../../../test/webapps/README.md) to
+generate a minimal set of test cases that produce the maximum amount of code
+coverage. The script will output in stdout tests cases to add and remove, and
+the developer then performs the instructed changes to make the testing
+implementation up-to-date.
 
 
 This suite of tests has two main parts:
@@ -62,20 +73,13 @@
 ### Script
 See the [README](../../../../test/webapps/README.md) and [design doc](https://docs.google.com/document/d/e/2PACX-1vTFI0sXhZMvvg1B3sctYVUe64WbLVNzuXFUa6f3XyYTzKs2JnuFR8qKNyXYZsxE-rPPvsq__4ZCyrcS/pub).
 
-### Test implementation
+### Test Structure
 The high level flow of execution is as follows:
- * Read the input file which contains the test cases
- * Parse the test cases into an `std::vector<std::vector<std::string>>`
- * Pass the vector of test cases to a parameterized test using
-   `testing::ValuesIn()`, which will run a test for each line in the input
-   file
- * Each test will loop over the testing actions, calling
-   `ExecuteAction(action_string)`
- * `ExecuteAction()` will switch on the string, and call the appropriate
-   action implementation method
- * A state snapshot will be captured after non-inspection (state mutating)
-   actions (so inspection actions can assert various state changes)
-
+ * Each tests lives as a regular browsertest with a specific name so the script
+   can determine if it exists.
+ * The script outputs a test where actions are method calls, and before & after
+   each test there is Pre and Post action method to call allow the framework to
+   record state, clean up, or wait as necessary.
 
 ## Components
 [Design
@@ -84,11 +88,9 @@
 ### WebAppIntegrationBrowserTestBase
 A helper class containing most of the test implementation, meant to be used
 as a private member on the test-driving classes. Contains most of the test
-implementation:
- * Input file parser
- * ExecuteAction()
- * Most action implementation methods
- * Capturing state snapshots
+framework implementation:
+ * Most action implementation methods.
+ * Capturing state snapshots.
 
 ### WebAppIntegrationBrowserTestBase::TestDelegate
 An abstract class that’s an application of the delegate interface pattern.
@@ -100,14 +102,13 @@
 in `TwoClientWebAppsSyncTest`, but called from the base class.
 
 ### WebAppIntegrationBrowserTest
-Subclass of both `InProcessBrowserTest` and
-`WebAppIntegrationBrowserTestBase::TestDelegate`. Drives the test by
-calling `IN_PROC_BROWSER_TEST_P` and instantiating the parameterized
-test as described above. Responsible for telling the base class where the test
-input files live, handling test setup, and implementing `TestDelegate` methods
-to expose protected members of `InProcessBrowserTest` to the base class. This
-class owns the base class, and stores it as a private member, `helper_`, passing
-it an instance of itself (as the TestDelegate) on construction.
+Class that drives the tests. Subclass of both `InProcessBrowserTest` and
+`WebAppIntegrationBrowserTestBase::TestDelegate`. Responsible
+for telling the base class where the test input files live, handling test setup,
+and implementing `TestDelegate` methods to expose protected members of
+`InProcessBrowserTest` to the base class. This class owns the base class, and
+stores it as a private member, `helper_`, passing it an instance of itself (as
+the TestDelegate) on construction.
 
 
 ### TwoClientWebAppsSyncTest
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 199f1f4..9847454 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -1193,6 +1193,29 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+// Ensure that web app windows don't duplicate the app name in the title, when
+// the page's title already starts with the app name.
+IN_PROC_BROWSER_TEST_P(WebAppBrowserTest_PrefixInTitle, PrefixExistsInTitle) {
+  const GURL app_url =
+      https_server()->GetURL("app.com", "/web_apps/title_appname_prefix.html");
+  const std::u16string app_title = u"A Web App";
+
+  auto web_app_info = std::make_unique<WebApplicationInfo>();
+  web_app_info->start_url = app_url;
+  web_app_info->scope = app_url.GetWithoutFilename();
+  web_app_info->title = app_title;
+  const AppId app_id = InstallWebApp(std::move(web_app_info));
+
+  Browser* const app_browser = LaunchWebAppBrowser(app_id);
+  content::WebContents* const web_contents =
+      app_browser->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+
+  // The window title should not repeat "A Web App".
+  EXPECT_EQ(u"A Web App - funny cat video",
+            app_browser->GetWindowTitleForCurrentTab(false));
+}
+
 // Ensure that web app windows with blank titles don't display the URL as a
 // default window title.
 IN_PROC_BROWSER_TEST_P(WebAppBrowserTest_PrefixInTitle,
diff --git a/chrome/common/media/cdm_registration.cc b/chrome/common/media/cdm_registration.cc
index 421d7d8..28107be 100644
--- a/chrome/common/media/cdm_registration.cc
+++ b/chrome/common/media/cdm_registration.cc
@@ -235,6 +235,12 @@
 
 void AddHardwareSecureWidevine(std::vector<content::CdmInfo>* cdms) {
 #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosUseChromeosProtectedMedia)) {
+    return;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   media::CdmCapability capability;
 
   // The following audio formats are supported for decrypt-only.
@@ -249,7 +255,15 @@
   capability.video_codecs.emplace(media::VideoCodec::kCodecH264, kAllProfiles);
 #endif
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosEnablePlatformHevc)) {
+    capability.video_codecs.emplace(media::VideoCodec::kCodecHEVC,
+                                    kAllProfiles);
+  }
+#else
   capability.video_codecs.emplace(media::VideoCodec::kCodecHEVC, kAllProfiles);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 #endif
 
   // Both encryption schemes are supported on ChromeOS.
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc
index f0e1a2d..56364b0 100644
--- a/chrome/renderer/media/chrome_key_systems.cc
+++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/logging.h"
@@ -172,6 +173,13 @@
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
 SupportedCodecs GetHevcCodecs(
     const std::vector<media::VideoCodecProfile>& profiles) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosEnablePlatformHevc)) {
+    return media::EME_CODEC_NONE;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
   // If no profiles are specified, then all are supported.
   if (profiles.empty()) {
     return media::EME_CODEC_HEVC_PROFILE_MAIN |
diff --git a/chrome/services/sharing/nearby/nearby_connections.cc b/chrome/services/sharing/nearby/nearby_connections.cc
index 1677aa8..4a901b62 100644
--- a/chrome/services/sharing/nearby/nearby_connections.cc
+++ b/chrome/services/sharing/nearby/nearby_connections.cc
@@ -564,7 +564,8 @@
             if (!remote)
               return;
 
-            DCHECK_GE(info.total_bytes, 0);
+            // TODO(crbug.com/1237525): Investigate if OnPayloadTransferUpdate()
+            // should not be called if |info.total_bytes| is negative.
             DCHECK_GE(info.bytes_transferred, 0);
             remote->OnPayloadTransferUpdate(
                 endpoint_id,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 99cfd40..d3e9a6a8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7576,6 +7576,13 @@
       "../browser/ui/webui/management/management_ui_handler_unittest.cc",
     ]
   }
+
+  if (is_win || is_linux || is_chromeos) {
+    sources += [
+      "../browser/lens/region_search/lens_region_search_controller_unittest.cc",
+    ]
+    deps += [ "//chrome/browser/lens/region_search" ]
+  }
 }
 
 static_library("test_support_unit") {
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js
index 02e34b8..13ac37f9 100644
--- a/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js
+++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js
@@ -10,7 +10,12 @@
       rootNode.addEventListener('focus', function(event) {
         if (event.target.root.url == url) {
           chrome.automation.getFocus(function(focus) {
-            assertEq('textField', focus.role);
+            if (focus.role !== 'textField') {
+              // If the page is particularly slow in loading, the root may have
+              // focus first. Wait for subsequent focus events.
+              return;
+            }
+
             assertEq('abc', focus.name);
             chrome.test.succeed();
           });
diff --git a/chrome/test/data/web_apps/TestExpectations b/chrome/test/data/web_apps/TestExpectations
deleted file mode 100644
index 6c52bbc..0000000
--- a/chrome/test/data/web_apps/TestExpectations
+++ /dev/null
@@ -1,8 +0,0 @@
-# Format:
-# crbug.com/bug_id [Platform] [Expectation] test_case
-#
-# Supported Platforms: ChromeOS, Linux, Mac, Win
-# Supported Expectations: Skip
-
-# Add support for |uninstall_from_menu| on CrOS
-crbug.com/1159651 [ ChromeOS ] [ Skip ] navigate_installable, install_omnibox_or_menu, launch_internal, uninstall_from_menu, navigate_browser_in_scope, check_install_icon_shown,check_launch_icon_not_shown,
diff --git a/chrome/test/data/web_apps/title_appname_prefix.html b/chrome/test/data/web_apps/title_appname_prefix.html
new file mode 100644
index 0000000..7c72118
--- /dev/null
+++ b/chrome/test/data/web_apps/title_appname_prefix.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>A Web App - funny cat video</title>
+</head>
+<body>
+  <h1>Content</h1>
+</body>
+</html>
diff --git a/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv b/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv
deleted file mode 100644
index f7927945..0000000
--- a/chrome/test/data/web_apps/web_app_integration_browsertest_cases.csv
+++ /dev/null
@@ -1,52 +0,0 @@
-add_policy_app_internal_tabbed_site_a, check_manifest_display_mode_browser_internal,
-add_policy_app_internal_tabbed_site_a, list_apps_internal
-add_policy_app_internal_tabbed_site_a, navigate_browser_in_scope_site_a, check_launch_icon_not_shown, check_install_icon_shown
-add_policy_app_internal_tabbed_site_a, uninstall_policy_app, list_apps_internal, check_app_not_in_list_site_a,
-add_policy_app_internal_windowed_site_a, check_manifest_display_mode_standalone_internal,
-navigate_installable_site_a, check_install_icon_shown, check_launch_icon_not_shown,
-navigate_installable_site_a, check_install_icon_shown, install_omnibox, check_window_created, launch_internal_site_a, close_pwa, check_no_crash,
-navigate_installable_site_a, check_installable, install_omnibox, navigate_browser_in_scope_site_a, check_launch_icon_shown, check_install_icon_not_shown,
-navigate_installable_site_a, install_create_shortcut_tabbed, list_apps_internal, set_open_in_window_internal_site_a, launch_internal_site_a, check_window_created, navigate_browser_in_scope_site_a, check_launch_icon_shown
-navigate_installable_site_a, install_create_shortcut_tabbed, list_apps_internal, set_open_in_window_internal_site_a, list_apps_internal
-navigate_installable_site_a, install_create_shortcut_tabbed, list_apps_internal, set_open_in_window_internal_site_a, navigate_browser_in_scope_site_a, check_launch_icon_shown
-navigate_installable_site_a, install_create_shortcut_tabbed, set_open_in_window_internal_site_a, launch_internal_site_a, check_window_created,
-navigate_installable_site_a, install_create_shortcut_tabbed, set_open_in_window_internal_site_a, launch_internal_site_a, check_window_created, navigate_browser_in_scope_site_a, check_launch_icon_shown
-navigate_installable_site_a, install_create_shortcut_tabbed, set_open_in_window_internal_site_a, list_apps_internal
-navigate_installable_site_a, install_create_shortcut_tabbed, set_open_in_window_internal_site_a, navigate_browser_in_scope_site_a, check_launch_icon_shown
-navigate_installable_site_a, install_internal_windowed_site_a, launch_internal_site_a, check_window_created, navigate_browser_in_scope_site_a, check_install_icon_not_shown, check_launch_icon_shown, close_pwa, check_no_crash
-navigate_installable_site_a, install_internal_windowed_site_a, list_apps_internal, set_open_in_tab_internal_site_a, launch_internal_site_a, check_tab_created, check_install_icon_shown
-navigate_installable_site_a, install_internal_windowed_site_a, list_apps_internal, set_open_in_tab_internal_site_a, list_apps_internal
-navigate_installable_site_a, install_internal_windowed_site_a, list_apps_internal, set_open_in_tab_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_internal_windowed_site_a, navigate_browser_in_scope_site_a, check_install_icon_not_shown, check_launch_icon_shown
-navigate_installable_site_a, install_internal_windowed_site_a, set_open_in_tab_internal_site_a, launch_internal_site_a, check_tab_created, check_install_icon_shown
-navigate_installable_site_a, install_internal_windowed_site_a, set_open_in_tab_internal_site_a, list_apps_internal
-navigate_installable_site_a, install_internal_windowed_site_a, set_open_in_tab_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-Linux, Mac, Win | navigate_installable_site_a, install_internal_windowed_site_a, uninstall_from_menu, list_apps_internal, check_app_not_in_list_site_a
-ChromeOS | navigate_installable_site_a, install_internal_windowed_site_a, uninstall_internal_site_a, list_apps_internal, check_app_not_in_list_site_a
-Linux, Mac, Win | navigate_installable_site_a, install_internal_windowed_site_a, uninstall_from_menu, navigate_browser_in_scope_site_a, check_install_icon_shown
-ChromeOS | navigate_installable_site_a, install_internal_windowed_site_a, uninstall_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_internal_windowed_site_a, uninstall_internal_site_a, list_apps_internal, check_app_not_in_list_site_a
-navigate_installable_site_a, install_internal_windowed_site_a, uninstall_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_omnibox, check_window_created, launch_internal_site_a, check_install_icon_not_shown, navigate_browser_in_scope_site_a, check_launch_icon_shown, close_pwa, check_no_crash
-navigate_installable_site_a, install_omnibox, check_window_created, list_apps_internal, set_open_in_tab_internal_site_a, launch_internal_site_a, check_tab_created, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_omnibox, check_window_created, list_apps_internal, set_open_in_tab_internal_site_a, list_apps_internal
-navigate_installable_site_a, install_omnibox, check_window_created, list_apps_internal, set_open_in_tab_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_omnibox, check_window_created, navigate_browser_in_scope_site_a, check_install_icon_not_shown, check_launch_icon_shown
-navigate_installable_site_a, install_omnibox, check_window_created, set_open_in_tab_internal_site_a, launch_internal_site_a, check_tab_created, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_omnibox, check_window_created, set_open_in_tab_internal_site_a, list_apps_internal
-navigate_installable_site_a, install_omnibox, check_window_created, set_open_in_tab_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-Linux, Mac, Win | navigate_installable_site_a, install_omnibox, check_window_created, uninstall_from_menu, list_apps_internal, check_app_not_in_list_site_a
-ChromeOS | navigate_installable_site_a, install_omnibox, check_window_created, uninstall_internal_site_a, list_apps_internal, check_app_not_in_list_site_a
-Linux, Mac, Win | navigate_installable_site_a, install_omnibox, check_window_created, uninstall_from_menu, navigate_browser_in_scope_site_a, check_install_icon_shown
-ChromeOS | navigate_installable_site_a, install_omnibox, check_window_created, uninstall_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_installable_site_a, install_omnibox, check_window_created, uninstall_internal_site_a, list_apps_internal, check_app_not_in_list_site_a
-navigate_installable_site_a, install_omnibox, check_window_created, uninstall_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-Linux, Mac, Win | navigate_installable_site_a, install_omnibox, launch_internal_site_a, uninstall_from_menu, navigate_browser_in_scope_site_a, check_install_icon_shown, check_launch_icon_not_shown,
-ChromeOS | navigate_installable_site_a, install_omnibox, launch_internal_site_a, uninstall_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown, check_launch_icon_not_shown,
-navigate_installable_site_a, install_omnibox, list_apps_internal, set_open_in_tab_internal_site_a, launch_internal_site_a, check_tab_created,
-navigate_installable_site_a, install_omnibox, list_apps_internal, set_open_in_tab_internal_site_a, check_user_display_mode_browser_internal,
-navigate_installable_site_a, install_omnibox, list_apps_internal, set_open_in_tab_internal_site_a, list_apps_internal, launch_internal_site_a, check_tab_created
-navigate_installable_site_a, install_omnibox, list_apps_internal, set_open_in_tab_internal_site_a, navigate_browser_in_scope_site_a, check_install_icon_shown
-navigate_not_installable, check_install_icon_not_shown,
-navigate_installable_site_a, install_omnibox, check_window_display_standalone, manifest_update_display_minimal_site_a, close_pwa, launch_internal_site_a, check_window_display_minimal
-navigate_installable_site_a, install_omnibox, check_window_display_standalone, close_pwa, manifest_update_display_minimal_site_a, launch_internal_site_a, check_window_display_minimal
diff --git a/chrome/test/data/web_apps/web_app_integration_browsertest_sync_cases.csv b/chrome/test/data/web_apps/web_app_integration_browsertest_sync_cases.csv
deleted file mode 100644
index 1286bcd..0000000
--- a/chrome/test/data/web_apps/web_app_integration_browsertest_sync_cases.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-Linux, Mac, Win | user_signin_internal, navigate_installable, sync_turned_off, install_omnibox, list_apps_internal, check_manifest_display_mode_standalone_internal, sync_turned_on, switch_profile_clients, list_apps_internal, check_app_not_locally_installed_internal, install_locally_internal, check_app_locally_installed_internal, check_manifest_display_mode_standalone_internal
-ChromeOS | user_signin_internal, navigate_installable, sync_turned_off, install_omnibox, list_apps_internal, check_manifest_display_mode_standalone_internal, sync_turned_on, switch_profile_clients, check_manifest_display_mode_standalone_internal
diff --git a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
index e643256..d3e9377 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
@@ -16,6 +16,7 @@
     ":local_images_element_test",
     ":personalization_app_test_utils",
     ":personalization_app_unified_test",
+    ":personalization_toast_element_test",
     ":test_mojo_interface_provider",
     ":test_personalization_store",
     ":wallpaper_collections_element_test",
@@ -36,6 +37,14 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
+js_library("personalization_toast_element_test") {
+  deps = [
+    ":test_personalization_store",
+    "//chromeos/components/personalization_app/resources/trusted:personalization_toast_element",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
 js_library("personalization_app_test_utils") {
   deps = [
     ":test_mojo_interface_provider",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_unified_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_unified_test.js
index 1baef55..a598460 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_unified_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_unified_test.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {LocalImagesTest} from './local_images_element_test.js';
+import {PersonalizationToastTest} from './personalization_toast_element_test.js';
 import {WallpaperBreadcrumbTest} from './wallpaper_breadcrumb_element_test.js';
 import {WallpaperCollectionsTest} from './wallpaper_collections_element_test.js';
 import {WallpaperImagesTest} from './wallpaper_images_element_test.js';
@@ -14,6 +15,7 @@
 
 const testCases = [
   LocalImagesTest,
+  PersonalizationToastTest,
   WallpaperBreadcrumbTest,
   WallpaperCollectionsTest,
   WallpaperImagesTest,
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js
new file mode 100644
index 0000000..e578ca6
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js
@@ -0,0 +1,54 @@
+// 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 {ActionName} from 'chrome://personalization/trusted/personalization_actions.js';
+import {PersonalizationToastElement} from 'chrome://personalization/trusted/personalization_toast_element.js';
+import {assertEquals, assertTrue} from '../../chai_assert.js';
+import {flushTasks, waitAfterNextRender} from '../../test_util.m.js';
+import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
+import {TestPersonalizationStore} from './test_personalization_store.js';
+
+export function PersonalizationToastTest() {
+  /** @type {!HTMLElement} */
+  let personalizationToastElement;
+
+  /** @type {?TestPersonalizationStore} */
+  let personalizationStore = null;
+
+  setup(() => {
+    const mocks = baseSetup();
+    personalizationStore = mocks.personalizationStore;
+    personalizationToastElement = initElement(PersonalizationToastElement.is);
+  });
+
+  teardown(async () => {
+    await teardownElement(personalizationToastElement);
+    await flushTasks();
+  });
+
+  test('hidden when no error is present', async () => {
+    assertEquals('', personalizationToastElement.innerHTML);
+  });
+
+  test('visible when error is present', async () => {
+    personalizationStore.data.error = 'There was an error';
+    personalizationStore.notifyObservers();
+    await waitAfterNextRender(personalizationToastElement);
+    assertTrue(
+        !!personalizationToastElement.shadowRoot.getElementById('container'));
+    assertEquals(
+        personalizationStore.data.error,
+        personalizationToastElement.shadowRoot.querySelector('p').innerText);
+  });
+
+  test('deploys an dismiss action when dismiss is clicked', async () => {
+    personalizationStore.data.error = 'There was an error';
+    personalizationStore.notifyObservers();
+    await waitAfterNextRender(personalizationToastElement);
+
+    personalizationStore.expectAction(ActionName.DISMISS_ERROR);
+    personalizationToastElement.shadowRoot.querySelector('cr-button').click();
+    await personalizationStore.waitForAction(ActionName.DISMISS_ERROR);
+  });
+}
diff --git a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
index 9864f9e..15c3b16 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
@@ -338,6 +338,8 @@
     });
   }
 
+  removePage() {}
+
   completeMultiPageScan() {
     this.methodCalled('completeMultiPageScan');
   }
diff --git a/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js b/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js
index e5d5c5bb..9f29be9 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js
@@ -5,9 +5,34 @@
 // TODO(crbug.com/1010321): Use cros_bluetooth_config.mojom-webui.js instead
 // as non-module JS is deprecated.
 import 'chrome://resources/mojo/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-lite.js';
+
+import {stringToMojoString16} from 'chrome://resources/cr_components/chromeos/bluetooth/bluetooth_utils.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 
 /**
+ * @param {string} id
+ * @param {string} publicName
+ * @param {boolean} connected
+ * @param {string|undefined} nickname
+ * @return {!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties}
+ */
+export function createDefaultBluetoothDevice(
+    id, publicName, connected, nickname = undefined) {
+  const mojom = chromeos.bluetoothConfig.mojom;
+  return {
+    deviceProperties: {
+      id: id,
+      publicName: stringToMojoString16(publicName),
+      deviceType: mojom.DeviceType.kComputer,
+      audioCapability: mojom.AudioOutputCapability.kNotCapableOfAudio,
+      connectionState: connected ? mojom.DeviceConnectionState.kConnected :
+                                   mojom.DeviceConnectionState.kNotConnected,
+    },
+    nickname: nickname,
+  };
+}
+
+/**
  * @fileoverview Fake implementation of CrosBluetoothConfig for testing.
  */
 
@@ -64,9 +89,7 @@
    * @param {chromeos.bluetoothConfig.mojom.BluetoothSystemState} systemState
    */
   setSystemState(systemState) {
-    const newSystemProperties = {...this.systemProperties_};
-    newSystemProperties.systemState = systemState;
-    this.systemProperties_ = newSystemProperties;
+    this.systemProperties_.systemState = systemState;
     this.notifyObserversPropertiesUpdated_();
   }
 
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js
index 212c33e..189a6d7 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js
@@ -9,7 +9,7 @@
 
 // #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {assertTrue} from '../../../chai_assert.js';
-// #import {FakeBluetoothConfig} from './fake_bluetooth_config.m.js';
+// #import {createDefaultBluetoothDevice, FakeBluetoothConfig} from './fake_bluetooth_config.m.js';
 // #import {setBluetoothConfigForTesting} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
 // clang-format on
 
@@ -25,7 +25,12 @@
    */
   let propertiesObserver;
 
+  /** @type {!chromeos.bluetoothConfig.mojom} */
+  let mojom;
+
   setup(function() {
+    mojom = chromeos.bluetoothConfig.mojom;
+
     bluetoothConfig = new FakeBluetoothConfig();
     setBluetoothConfigForTesting(bluetoothConfig);
     bluetoothDevicesSubpage =
@@ -97,9 +102,40 @@
     assertToggleEnabledState(/*enabled=*/ true);
 
     // Mock systemState becoming unavailable.
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kUnavailable);
+    bluetoothConfig.setSystemState(mojom.BluetoothSystemState.kUnavailable);
     await flushAsync();
     assertTrue(enableBluetoothToggle.disabled);
   });
+
+  test('Device lists states', async function() {
+    const getDeviceList = (connected) => {
+      return bluetoothDevicesSubpage.shadowRoot.querySelector(
+          connected ? '#connectedDeviceList' : '#unconnectedDeviceList');
+    };
+    // No lists should be showing at first.
+    assertFalse(!!getDeviceList(/*connected=*/ true));
+    assertFalse(!!getDeviceList(/*connected=*/ false));
+
+    const connectedDevice = createDefaultBluetoothDevice(
+        /*id=*/ '123456789', /*publicName=*/ 'BeatsX', /*connected=*/ true);
+    const unconnectedDevice = createDefaultBluetoothDevice(
+        /*id=*/ '987654321', /*publicName=*/ 'MX 3', /*connected=*/ false);
+
+    // Pair connected device.
+    bluetoothConfig.appendToPairedDeviceList([connectedDevice]);
+    await flushAsync();
+
+    assertTrue(!!getDeviceList(/*connected=*/ true));
+    assertEquals(getDeviceList(/*connected=*/ true).devices.length, 1);
+    assertFalse(!!getDeviceList(/*connected=*/ false));
+
+    // Pair unconnected device
+    bluetoothConfig.appendToPairedDeviceList([unconnectedDevice]);
+    await flushAsync();
+
+    assertTrue(!!getDeviceList(/*connected=*/ true));
+    assertEquals(getDeviceList(/*connected=*/ true).devices.length, 1);
+    assertTrue(!!getDeviceList(/*connected=*/ false));
+    assertEquals(getDeviceList(/*connected=*/ false).devices.length, 1);
+  });
 });
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js b/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js
index 57a03fb..5dc1a3a 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js
@@ -10,9 +10,9 @@
 // #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {Router, Route, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {assertTrue} from '../../../chai_assert.js';
-// #import {FakeBluetoothConfig} from './fake_bluetooth_config.m.js';
+// #import {createDefaultBluetoothDevice, FakeBluetoothConfig,} from './fake_bluetooth_config.m.js';
 // #import {setBluetoothConfigForTesting} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
-// #import {stringToMojoString16, mojoString16ToString} from 'chrome://resources/cr_components/chromeos/bluetooth/bluetooth_utils.js';
+// #import {mojoString16ToString} from 'chrome://resources/cr_components/chromeos/bluetooth/bluetooth_utils.js';
 // clang-format on
 
 suite('OsBluetoothSummaryTest', function() {
@@ -134,37 +134,14 @@
     // Bluetooth Icon should be default because no devices are connected.
     assertEquals('cr:bluetooth', getBluetoothStatusIcon().icon);
 
-    const device1 = {
-      deviceProperties: {
-        id: '123456789',
-        publicName: stringToMojoString16('BeatsX'),
-        deviceType: mojom.DeviceType.kComputer,
-        audioCapability: mojom.AudioOutputCapability.kNotCapableOfAudio,
-        connectionState: mojom.DeviceConnectionState.kConnected,
-      },
-      nickname: 'device1'
-    };
-
-    const device2 = {
-      deviceProperties: {
-        id: '987654321',
-        publicName: stringToMojoString16('MX master 3'),
-        deviceType: mojom.DeviceType.kComputer,
-        audioCapability: mojom.AudioOutputCapability.kNotCapableOfAudio,
-        connectionState: mojom.DeviceConnectionState.kConnected,
-      },
-    };
-
-    const device3 = {
-      deviceProperties: {
-        id: '456789',
-        publicName: stringToMojoString16('Radio head'),
-        deviceType: mojom.DeviceType.kMouse,
-        audioCapability: mojom.AudioOutputCapability.kNotCapableOfAudio,
-        connectionState: mojom.DeviceConnectionState.kConnected,
-      },
-      nickname: 'device3'
-    };
+    const device1 = createDefaultBluetoothDevice(
+        /*id=*/ '123456789', /*publicName=*/ 'BeatsX', /*connected=*/ true,
+        /*nickname=*/ 'device1');
+    const device2 = createDefaultBluetoothDevice(
+        /*id=*/ '987654321', /*publicName=*/ 'MX 3', /*connected=*/ true);
+    const device3 = createDefaultBluetoothDevice(
+        /*id=*/ '456789', /*publicName=*/ 'Radio head', /*connected=*/ true,
+        /*nickname=*/ 'device3');
 
     const mockPairedBluetoothDeviceProperties = [
       device1,
diff --git a/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_tests.js b/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_tests.js
index 928f30b4..cddc110 100644
--- a/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_tests.js
@@ -10,6 +10,7 @@
 // #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {assertTrue, assertEquals} from '../../../chai_assert.js';
 // #import {eventToPromise} from 'chrome://test/test_util.m.js';
+// #import {createDefaultBluetoothDevice} from './fake_bluetooth_config.m.js';
 // clang-format on
 
 suite('OsPairedBluetoothListTest', function() {
@@ -34,8 +35,10 @@
   });
 
   test('Device list change renders items correctly', async function() {
-    // TODO(crbug.com/1010321): Use real Device objects.
-    pairedBluetoothList.devices = [{}, {}, {}];
+    const device = createDefaultBluetoothDevice(
+        /*id=*/ '123456789', /*publicName=*/ 'BeatsX', /*connected=*/ true);
+
+    pairedBluetoothList.devices = [device, device, device];
     await flushAsync();
 
     const getListItems = () => {
@@ -46,7 +49,7 @@
 
     const ironResizePromise =
         test_util.eventToPromise('iron-resize', pairedBluetoothList);
-    pairedBluetoothList.devices = [{}, {}, {}, {}, {}];
+    pairedBluetoothList.devices = [device, device, device, device, device];
 
     await ironResizePromise;
     Polymer.dom.flush();
diff --git a/chrome/updater/app/server/win/service_main.cc b/chrome/updater/app/server/win/service_main.cc
index 670099a..e2c2ffd 100644
--- a/chrome/updater/app/server/win/service_main.cc
+++ b/chrome/updater/app/server/win/service_main.cc
@@ -7,6 +7,7 @@
 #include <atlsecurity.h>
 #include <sddl.h>
 
+#include <string>
 #include <type_traits>
 
 #include "base/command_line.h"
@@ -19,6 +20,7 @@
 #include "chrome/updater/app/server/win/server.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/win/win_constants.h"
+#include "chrome/updater/win/win_util.h"
 #include "chrome/updater/win/wrl_module.h"
 
 namespace updater {
@@ -87,10 +89,10 @@
 ServiceMain::~ServiceMain() = default;
 
 int ServiceMain::RunAsService() {
-  const wchar_t* const kServiceName =
-      IsInternalService() ? kWindowsInternalServiceName : kWindowsServiceName;
-  static const SERVICE_TABLE_ENTRY dispatch_table[] = {
-      {const_cast<LPTSTR>(kServiceName), &ServiceMain::ServiceMainEntry},
+  const std::wstring service_name = GetServiceName(IsInternalService());
+  const SERVICE_TABLE_ENTRY dispatch_table[] = {
+      {const_cast<LPTSTR>(service_name.c_str()),
+       &ServiceMain::ServiceMainEntry},
       {nullptr, nullptr}};
 
   if (!::StartServiceCtrlDispatcher(dispatch_table)) {
@@ -102,10 +104,9 @@
 }
 
 void ServiceMain::ServiceMainImpl() {
-  const wchar_t* const kServiceName =
-      IsInternalService() ? kWindowsInternalServiceName : kWindowsServiceName;
-  service_status_handle_ = ::RegisterServiceCtrlHandler(
-      kServiceName, &ServiceMain::ServiceControlHandler);
+  service_status_handle_ =
+      ::RegisterServiceCtrlHandler(GetServiceName(IsInternalService()).c_str(),
+                                   &ServiceMain::ServiceControlHandler);
   if (service_status_handle_ == nullptr) {
     PLOG(ERROR) << "RegisterServiceCtrlHandler failed";
     return;
diff --git a/chrome/updater/app/server/win/service_main.h b/chrome/updater/app/server/win/service_main.h
index 43c4e74..d7997d4 100644
--- a/chrome/updater/app/server/win/service_main.h
+++ b/chrome/updater/app/server/win/service_main.h
@@ -9,6 +9,7 @@
 
 #include "base/no_destructor.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/win/atl.h"
 
 namespace base {
 
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index 0ec48904..14d90ae 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -33,6 +33,7 @@
 #include "chrome/updater/util.h"
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/win_constants.h"
+#include "chrome/updater/win/win_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -161,9 +162,8 @@
   }
 
   if (scope == UpdaterScope::kSystem) {
-    for (const wchar_t* const service_name :
-         {kWindowsInternalServiceName, kWindowsServiceName}) {
-      EXPECT_TRUE(DeleteService(service_name));
+    for (const bool is_internal_service : {true, false}) {
+      EXPECT_TRUE(DeleteService(GetServiceName(is_internal_service).c_str()));
     }
   }
 
@@ -231,9 +231,8 @@
   }
 
   if (scope == UpdaterScope::kSystem) {
-    for (const wchar_t* const service_name :
-         {kWindowsInternalServiceName, kWindowsServiceName}) {
-      EXPECT_TRUE(IsServiceGone(service_name));
+    for (const bool is_internal_service : {true, false}) {
+      EXPECT_TRUE(IsServiceGone(GetServiceName(is_internal_service).c_str()));
     }
   }
 
diff --git a/chrome/updater/win/setup/setup_util.cc b/chrome/updater/win/setup/setup_util.cc
index 4753cd4..73a438d 100644
--- a/chrome/updater/win/setup/setup_util.cc
+++ b/chrome/updater/win/setup/setup_util.cc
@@ -32,6 +32,7 @@
 #include "chrome/updater/util.h"
 #include "chrome/updater/win/task_scheduler.h"
 #include "chrome/updater/win/win_constants.h"
+#include "chrome/updater/win/win_util.h"
 
 // Specialization for std::hash so that IID instances can be stored in an
 // associative container. This implementation of the hash function adds
@@ -216,10 +217,9 @@
   com_service_command.AppendSwitch(kEnableLoggingSwitch);
   com_service_command.AppendSwitchASCII(kLoggingModuleSwitch,
                                         "*/chrome/updater/*=2");
-  const wchar_t* const kServiceName =
-      internal_service ? kWindowsInternalServiceName : kWindowsServiceName;
   list->AddWorkItem(new installer::InstallServiceWorkItem(
-      kServiceName, kServiceName, com_service_command,
+      GetServiceName(internal_service).c_str(),
+      GetServiceDisplayName(internal_service).c_str(), com_service_command,
       base::ASCIIToWide(UPDATER_KEY),
       internal_service ? GetSideBySideServers(UpdaterScope::kSystem)
                        : GetActiveServers(UpdaterScope::kSystem),
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc
index 0ee4ee8..c4f31dd 100644
--- a/chrome/updater/win/setup/uninstall.cc
+++ b/chrome/updater/win/setup/uninstall.cc
@@ -31,6 +31,7 @@
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/task_scheduler.h"
 #include "chrome/updater/win/win_constants.h"
+#include "chrome/updater/win/win_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
@@ -57,10 +58,10 @@
                                    WorkItem::kWow64Default);
   }
 
-  for (const wchar_t* const service_name :
-       {kWindowsInternalServiceName, kWindowsServiceName}) {
+  for (const bool is_internal_service : {true, false}) {
+    const std::wstring service_name = GetServiceName(is_internal_service);
     if (!installer::InstallServiceWorkItem::DeleteService(
-            service_name, base::ASCIIToWide(UPDATER_KEY), {}, {})) {
+            service_name.c_str(), base::ASCIIToWide(UPDATER_KEY), {}, {})) {
       LOG(WARNING) << "DeleteService [" << service_name << "] failed.";
     }
   }
diff --git a/chrome/updater/win/win_constants.cc b/chrome/updater/win/win_constants.cc
index 55d48ad..269e483 100644
--- a/chrome/updater/win/win_constants.cc
+++ b/chrome/updater/win/win_constants.cc
@@ -29,7 +29,7 @@
 const wchar_t kRegKeyCompanyEnrollment[] = COMPANY_KEY L"Enrollment\\";
 const wchar_t kRegValueDmToken[] = L"dmtoken";
 
-const wchar_t kWindowsServiceName[] = L"UpdaterService";
-const wchar_t kWindowsInternalServiceName[] = L"UpdaterInternalService";
+const wchar_t kWindowsServiceName[] = L"Service";
+const wchar_t kWindowsInternalServiceName[] = L"InternalService";
 
 }  // namespace updater
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc
index c1652847..5fbc7c5 100644
--- a/chrome/updater/win/win_util.cc
+++ b/chrome/updater/win/win_util.cc
@@ -30,7 +30,9 @@
 #include "base/win/registry.h"
 #include "base/win/scoped_handle.h"
 #include "chrome/updater/constants.h"
+#include "chrome/updater/updater_branding.h"
 #include "chrome/updater/updater_scope.h"
+#include "chrome/updater/updater_version.h"
 #include "chrome/updater/win/user_info.h"
 #include "chrome/updater/win/win_constants.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -539,4 +541,20 @@
 
   return s;
 }
+
+std::wstring GetServiceName(bool is_internal_service) {
+  std::wstring service_name = GetServiceDisplayName(is_internal_service);
+  service_name.erase(
+      std::remove_if(service_name.begin(), service_name.end(), isspace),
+      service_name.end());
+  return service_name;
+}
+
+std::wstring GetServiceDisplayName(bool is_internal_service) {
+  return base::StrCat(
+      {base::ASCIIToWide(PRODUCT_FULLNAME_STRING), L" ",
+       is_internal_service ? kWindowsInternalServiceName : kWindowsServiceName,
+       L" ", kUpdaterVersionUtf16});
+}
+
 }  // namespace updater
diff --git a/chrome/updater/win/win_util.h b/chrome/updater/win/win_util.h
index 438c9f4..5f35943 100644
--- a/chrome/updater/win/win_util.h
+++ b/chrome/updater/win/win_util.h
@@ -127,6 +127,16 @@
 // caller. The value can be used for logging purposes.
 std::string GetUACState();
 
+// Returns the versioned service name in the following format:
+// "{ProductName}{InternalService/Service}{UpdaterVersion}".
+// For instance: "ChromiumUpdaterInternalService92.0.0.1".
+std::wstring GetServiceName(bool is_internal_service);
+
+// Returns the versioned service name in the following format:
+// "{ProductName} {InternalService/Service} {UpdaterVersion}".
+// For instance: "ChromiumUpdater InternalService 92.0.0.1".
+std::wstring GetServiceDisplayName(bool is_internal_service);
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_WIN_WIN_UTIL_H_
diff --git a/chrome/updater/win/win_util_unittest.cc b/chrome/updater/win/win_util_unittest.cc
index c497537a..2b356cb 100644
--- a/chrome/updater/win/win_util_unittest.cc
+++ b/chrome/updater/win/win_util_unittest.cc
@@ -6,18 +6,23 @@
 
 #include <windows.h>
 
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/updater/updater_branding.h"
+#include "chrome/updater/updater_version.h"
+#include "chrome/updater/win/win_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace updater {
 
-TEST(UpdaterTestUtil, HRESULTFromLastError) {
+TEST(WinUtil, HRESULTFromLastError) {
   ::SetLastError(ERROR_ACCESS_DENIED);
   EXPECT_EQ(E_ACCESSDENIED, HRESULTFromLastError());
   ::SetLastError(ERROR_SUCCESS);
   EXPECT_EQ(E_FAIL, HRESULTFromLastError());
 }
 
-TEST(UpdaterTestUtil, GetDownloadProgress) {
+TEST(WinUtil, GetDownloadProgress) {
   EXPECT_EQ(GetDownloadProgress(0, 50), 0);
   EXPECT_EQ(GetDownloadProgress(12, 50), 24);
   EXPECT_EQ(GetDownloadProgress(25, 50), 50);
@@ -28,4 +33,24 @@
   EXPECT_EQ(GetDownloadProgress(50, 0), -1);
 }
 
+TEST(WinUtil, GetServiceDisplayName) {
+  for (const bool is_internal_service : {true, false}) {
+    EXPECT_EQ(base::StrCat({base::ASCIIToWide(PRODUCT_FULLNAME_STRING), L" ",
+                            is_internal_service ? kWindowsInternalServiceName
+                                                : kWindowsServiceName,
+                            L" ", kUpdaterVersionUtf16}),
+              GetServiceDisplayName(is_internal_service));
+  }
+}
+
+TEST(WinUtil, GetServiceName) {
+  for (const bool is_internal_service : {true, false}) {
+    EXPECT_EQ(base::StrCat({base::ASCIIToWide(PRODUCT_FULLNAME_STRING),
+                            is_internal_service ? kWindowsInternalServiceName
+                                                : kWindowsServiceName,
+                            kUpdaterVersionUtf16}),
+              GetServiceName(is_internal_service));
+  }
+}
+
 }  // namespace updater
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 7da5ea6..4270ba51 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -1621,6 +1621,12 @@
       <message name="IDS_PERSONALIZATION_APP_NETWORK_ERROR" desc="Label for the error page when wallpaper information cannot be downloaded">
         Please connect to a network and reload the page to view wallpaper.
       </message>
+      <message name="IDS_PERSONALIZATION_APP_SET_WALLPAPER_ERROR" desc="Label for the error toast notification when setting wallpaper failed">
+        There was an error. Please try again by choosing other images.
+      </message>
+      <message name="IDS_PERSONALIZATION_APP_DISMISS" desc="Label for the toast notification dismiss action">
+        Dismiss
+      </message>
 
       <!-- Traffic Counters UI -->
       <message name="IDS_TRAFFIC_COUNTERS_UNKNOWN" desc="Traffic counters related to an unknown source">
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_DISMISS.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_DISMISS.png.sha1
new file mode 100644
index 0000000..bd61536
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_DISMISS.png.sha1
@@ -0,0 +1 @@
+bdd66526d8292c8ffb346c7381f93cbaabee82fb
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SET_WALLPAPER_ERROR.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SET_WALLPAPER_ERROR.png.sha1
new file mode 100644
index 0000000..bd61536
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_SET_WALLPAPER_ERROR.png.sha1
@@ -0,0 +1 @@
+bdd66526d8292c8ffb346c7381f93cbaabee82fb
\ No newline at end of file
diff --git a/chromeos/components/eche_app_ui/test/eche_app_ui_chrome_branded_browsertest.js b/chromeos/components/eche_app_ui/test/eche_app_ui_chrome_branded_browsertest.js
index 30eee6f..5332c9af 100644
--- a/chromeos/components/eche_app_ui/test/eche_app_ui_chrome_branded_browsertest.js
+++ b/chromeos/components/eche_app_ui/test/eche_app_ui_chrome_branded_browsertest.js
@@ -33,19 +33,6 @@
   }
 };
 
-// Tests that chrome://eche-app goes somewhere instead of
-// 404ing or crashing.
-TEST_F('EcheAppUIBrowserTest', 'HasChromeSchemeURL', () => {
-  assertEquals(document.title, 'Eche');
-  assertEquals(document.location.origin, HOST_ORIGIN);
-  testDone();
-});
-
-// Tests that the implementations of echeapi.d.ts are defined.
-TEST_F('EcheAppUIBrowserTest', 'HasDefinedEcheapi', () => {
-  chai.assert.isDefined(echeapi.webrtc.registerSignalReceiver);
-  chai.assert.isDefined(echeapi.webrtc.sendSignal);
-  chai.assert.isDefined(echeapi.webrtc.tearDownSignal);
-  chai.assert.isDefined(echeapi.system.registerTabletModeChangedReceiver);
-  testDone();
-});
+// TODO(samchiu) Temporarily disable browser_test since a future Eche roll
+// will break the function of eche window. Test will be enable when we
+// phase in http://go/crrev/c/3081307.
diff --git a/chromeos/components/personalization_app/personalization_app_ui.cc b/chromeos/components/personalization_app/personalization_app_ui.cc
index 5110461..e783409 100644
--- a/chromeos/components/personalization_app/personalization_app_ui.cc
+++ b/chromeos/components/personalization_app/personalization_app_ui.cc
@@ -70,7 +70,11 @@
       {"unknownImageAttribution",
        IDS_PERSONALIZATION_APP_UNKNOWN_IMAGE_ATTRIBUTION},
       {"networkError", IDS_PERSONALIZATION_APP_NETWORK_ERROR},
-      {"ariaLabelLoading", IDS_PERSONALIZATION_APP_ARIA_LABEL_LOADING}};
+      {"ariaLabelLoading", IDS_PERSONALIZATION_APP_ARIA_LABEL_LOADING},
+      // Using old wallpaper app error string pending final revision.
+      // TODO(b/195609442)
+      {"setWallpaperError", IDS_PERSONALIZATION_APP_SET_WALLPAPER_ERROR},
+      {"dismiss", IDS_PERSONALIZATION_APP_DISMISS}};
   source->AddLocalizedStrings(kLocalizedStrings);
   source->UseStringsJs();
 }
diff --git a/chromeos/components/personalization_app/resources/trusted/BUILD.gn b/chromeos/components/personalization_app/resources/trusted/BUILD.gn
index 0deb47c..2fdc3a33 100644
--- a/chromeos/components/personalization_app/resources/trusted/BUILD.gn
+++ b/chromeos/components/personalization_app/resources/trusted/BUILD.gn
@@ -8,12 +8,13 @@
 
 polymer_element_files = [
   "local_images_element.js",
+  "personalization_router_element.js",
+  "personalization_toast_element.js",
   "wallpaper_breadcrumb_element.js",
   "wallpaper_collections_element.js",
   "wallpaper_error_element.js",
   "wallpaper_images_element.js",
   "wallpaper_selected_element.js",
-  "personalization_router_element.js",
 ]
 
 static_files = [
@@ -61,6 +62,7 @@
     ":personalization_reducers",
     ":personalization_router_element",
     ":personalization_store",
+    ":personalization_toast_element",
     ":styles",
     ":wallpaper_breadcrumb_element",
     ":wallpaper_collections_element",
@@ -103,6 +105,15 @@
   ]
 }
 
+js_library("personalization_toast_element") {
+  deps = [
+    ":personalization_actions",
+    ":personalization_store",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:load_time_data",
+  ]
+}
+
 js_library("personalization_store") {
   deps = [
     ":personalization_actions",
@@ -186,6 +197,7 @@
     ":personalization_reducers",
     ":personalization_router_element",
     ":personalization_store",
+    ":personalization_toast_element",
     ":styles",
     ":wallpaper_breadcrumb_element",
     ":wallpaper_collections_element",
diff --git a/chromeos/components/personalization_app/resources/trusted/personalization_actions.js b/chromeos/components/personalization_app/resources/trusted/personalization_actions.js
index 60823779..2f7fe8a 100644
--- a/chromeos/components/personalization_app/resources/trusted/personalization_actions.js
+++ b/chromeos/components/personalization_app/resources/trusted/personalization_actions.js
@@ -25,6 +25,7 @@
   SET_LOCAL_IMAGE_DATA: 'set_local_image_data',
   SET_SELECTED_IMAGE: 'set_selected_image',
   SET_UPDATED_DAILY_REFRESH_IMAGE: 'set_updated_daily_refreshed_image',
+  DISMISS_ERROR: 'dismiss_error',
 };
 
 
@@ -178,3 +179,10 @@
     name: ActionName.SET_SELECTED_IMAGE,
   };
 }
+
+/**
+ * @return {!Action}
+ */
+export function dismissErrorAction() {
+  return {name: ActionName.DISMISS_ERROR};
+}
diff --git a/chromeos/components/personalization_app/resources/trusted/personalization_app.js b/chromeos/components/personalization_app/resources/trusted/personalization_app.js
index eab0b741f..e5e7c85 100644
--- a/chromeos/components/personalization_app/resources/trusted/personalization_app.js
+++ b/chromeos/components/personalization_app/resources/trusted/personalization_app.js
@@ -11,6 +11,7 @@
 import '/strings.m.js';
 import './local_images_element.js';
 import './personalization_router_element.js';
+import './personalization_toast_element.js';
 import './wallpaper_breadcrumb_element.js';
 import './wallpaper_collections_element.js';
 import './wallpaper_error_element.js';
diff --git a/chromeos/components/personalization_app/resources/trusted/personalization_reducers.js b/chromeos/components/personalization_app/resources/trusted/personalization_reducers.js
index b5e1f55b..91b330e 100644
--- a/chromeos/components/personalization_app/resources/trusted/personalization_reducers.js
+++ b/chromeos/components/personalization_app/resources/trusted/personalization_reducers.js
@@ -97,6 +97,7 @@
  *   currentSelected: ?DisplayableImage,
  *   pendingSelected: ?DisplayableImage,
  *   dailyRefresh: !DailyRefreshState,
+ *   error: ?string,
  * }}
  */
 export let PersonalizationState;
@@ -120,6 +121,7 @@
     currentSelected: null,
     pendingSelected: null,
     dailyRefresh: {collectionId: null},
+    error: null,
   };
 }
 
@@ -321,6 +323,31 @@
   }
 }
 
+/**
+ * @param {?string} state
+ * @param {!Action} action
+ * @return {?string}
+ */
+function errorReducer(state, action) {
+  switch (action.name) {
+    case ActionName.END_SELECT_IMAGE:
+      const {success} =
+          /** @type {{name: string, success: boolean}} */ (action);
+      if (success) {
+        return null;
+      }
+      return loadTimeData.getString('setWallpaperError');
+    case ActionName.DISMISS_ERROR:
+      if (!state) {
+        console.warn(
+            'Received dismiss error action when error is already null');
+      }
+      return null;
+    default:
+      return state;
+  }
+}
+
 const root = combineReducers({
   backdrop: backdropReducer,
   loading: loadingReducer,
@@ -328,6 +355,7 @@
   currentSelected: currentSelectedReducer,
   pendingSelected: pendingSelectedReducer,
   dailyRefresh: dailyRefreshReducer,
+  error: errorReducer,
 });
 
 /**
diff --git a/chromeos/components/personalization_app/resources/trusted/personalization_router_element.html b/chromeos/components/personalization_app/resources/trusted/personalization_router_element.html
index 81c9664..c2d7b6f 100644
--- a/chromeos/components/personalization_app/resources/trusted/personalization_router_element.html
+++ b/chromeos/components/personalization_app/resources/trusted/personalization_router_element.html
@@ -10,6 +10,7 @@
       ". leftspacer .          rightspacer .";
     grid-template-columns: 1fr 16px minmax(568px, 920px) 16px 1fr;
     grid-template-rows: 56px 172px 12px 1fr 12px;
+    position: relative;
     width: 100%;
   }
   #leftspacer {
@@ -27,6 +28,13 @@
   wallpaper-collections, wallpaper-images, local-images {
     grid-area: imagegrid;
   }
+  personalization-toast {
+    position: absolute;
+    bottom: 16px;
+    left: 50%;
+    max-width: 380px;
+    transform: translateX(-50%);
+  }
 </style>
 <div id="container">
   <!-- dwell-time is set to 200ms to populate history state more quickly while
@@ -51,4 +59,5 @@
   <local-images hidden="[[!shouldShowLocalCollection_(path_)]]"></local-images>
   <!-- Prevent the right margin from collapsing when window gets very narrow -->
   <div id="rightspacer"></div>
+  <personalization-toast></personalization-toast>
 </div>
diff --git a/chromeos/components/personalization_app/resources/trusted/personalization_toast_element.html b/chromeos/components/personalization_app/resources/trusted/personalization_toast_element.html
new file mode 100644
index 0000000..65bf5f5
--- /dev/null
+++ b/chromeos/components/personalization_app/resources/trusted/personalization_toast_element.html
@@ -0,0 +1,68 @@
+<style>
+  /*
+    Cannot use semantic colors because toast notification element must invert
+    the color scheme. These are swapped versions of --cros-* variables.
+  */
+  :host {
+    --personalization-app-bg-color-elevation-2: rgb(46, 46, 49);
+    --personalization-app-text-color-primary: var(--google-grey-200);
+  }
+
+  /* Invert cr-button colors. These are normally dark mode colors. */
+  cr-button {
+    --ink-color: var(--google-blue-refresh-300);
+    --text-color: var(--google-blue-refresh-300);
+  }
+
+  /* Override some cr-button variables. */
+  cr-button {
+    --active-shadow-rgb: transparent;
+    --border-color: transparent;
+    --hover-border-color: transparent;
+    --hover-bg-color: transparent;
+    --hover-bg-action: transparent;
+    --cr-button-height: 36px;
+    border: 0;
+    margin: 0;
+    padding: 8px;
+  }
+
+  @media (prefers-color-scheme: dark) {
+    :host {
+      --personalization-app-bg-color-elevation-2: white;
+      --personalization-app-text-color-primary: var(--google-grey-900);
+    }
+
+    /* Invert cr-button colors. These are normally light mode colors. */
+    cr-button {
+      --ink-color: var(--google-blue-600);
+      --text-color: var(--google-blue-600);
+    }
+  }
+
+  #container {
+    background-color: var(--personalization-app-bg-color-elevation-2);
+    border-radius: 4px;
+    box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px rgba(0, 0, 0, 0.15);
+    box-sizing: border-box;
+    color: var(--personalization-app-text-color-primary);
+    display: flex;
+    flex-flow: row nowrap;
+    justify-content: space-between;
+    padding: 16px;
+  }
+
+  p {
+    margin: 0;
+    margin-inline-end: 16px;
+  }
+
+</style>
+<template is="dom-if" if="[[error_]]">
+  <div id="container">
+    <p>[[error_]]</p>
+    <cr-button on-click="onDismissClicked_">
+      [[i18n('dismiss')]]
+    </cr-button>
+  </div>
+</template>
diff --git a/chromeos/components/personalization_app/resources/trusted/personalization_toast_element.js b/chromeos/components/personalization_app/resources/trusted/personalization_toast_element.js
new file mode 100644
index 0000000..81d3fde
--- /dev/null
+++ b/chromeos/components/personalization_app/resources/trusted/personalization_toast_element.js
@@ -0,0 +1,50 @@
+// 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.
+
+/**
+ * @fileoverview This component displays toast notifications to the user.
+ */
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {dismissErrorAction} from './personalization_actions.js';
+import {WithPersonalizationStore} from './personalization_store.js';
+
+/** @polymer */
+export class PersonalizationToastElement extends WithPersonalizationStore {
+  static get is() {
+    return 'personalization-toast';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /**
+       * @type {?string}
+       * @private
+       */
+      error_: {
+        type: String,
+        value: null,
+      },
+    };
+  }
+
+  /** @override */
+  connectedCallback() {
+    super.connectedCallback();
+    this.watch('error_', state => state.error);
+  }
+
+  /** @private */
+  onDismissClicked_() {
+    this.dispatch(dismissErrorAction());
+  }
+}
+
+customElements.define(
+    PersonalizationToastElement.is, PersonalizationToastElement);
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 17e7895..48b721f 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -420,6 +420,16 @@
   kUseDlc = 2,
 };
 
+// Corresponds to BUILDFLAGs we use in ash-chrome that we want to propagate to
+// Lacros Chrome. On the Lacros side we turn this into command line switches.
+[Stable, Extensible]
+enum BuildFlag {
+  [Default] kUnknown = 0,
+  kUseChromeosProtectedMedia = 1,
+  kEnablePlatformEncryptedHevc = 2,
+  kEnablePlatformHevc = 3,
+};
+
 // BrowserInitParams is a set of parameters for initialization of browsers
 // (such as lacros-chrome), which is passed from ash-chrome to a browser.
 // Since ash-chrome and browsers may have different versions, the browsers must
@@ -438,7 +448,7 @@
 // processed by the browser.
 //
 // Next version: 24
-// Next id: 23
+// Next id: 24
 [Stable, RenamedFrom="crosapi.mojom.LacrosInitParams"]
 struct BrowserInitParams {
   // This is ash-chrome's version of the Crosapi interface. This is used by
@@ -580,6 +590,11 @@
   // Whether (and how) on-device handwriting recognition is supported, depending
   // ash-chrome's startup switches.
   OndeviceHandwritingSupport ondevice_handwriting_support@22;
+
+  [MinVersion=23]
+  // Build flags from ash-chrome that we turn into command line switches to
+  // enforce at run-time in lacros-chrome.
+  array<BuildFlag>? build_flags@23;
 };
 
 // BrowserService defines the APIs that live in a browser (such as
diff --git a/chromeos/services/assistant/public/mojom/BUILD.gn b/chromeos/services/assistant/public/mojom/BUILD.gn
index 84426f8..e83cb5b 100644
--- a/chromeos/services/assistant/public/mojom/BUILD.gn
+++ b/chromeos/services/assistant/public/mojom/BUILD.gn
@@ -6,4 +6,6 @@
 
 mojom("mojom") {
   sources = [ "assistant_audio_decoder.mojom" ]
+
+  public_deps = [ "//sandbox/policy/mojom" ]
 }
diff --git a/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom b/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom
index f80f079..a72ac76 100644
--- a/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom
+++ b/chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom
@@ -4,7 +4,10 @@
 
 module chromeos.assistant.mojom;
 
+import "sandbox/policy/mojom/sandbox.mojom";
+
 // A factory for creating an assistant audio decoder.
+[ServiceSandbox=sandbox.mojom.Sandbox.kUtility]
 interface AssistantAudioDecoderFactory {
   // Creates an AssistantAudioDecoder to decode audio stream data from
   // |data_source|.
diff --git a/chromeos/services/ime/public/mojom/BUILD.gn b/chromeos/services/ime/public/mojom/BUILD.gn
index 58c7198..f1d4725 100644
--- a/chromeos/services/ime/public/mojom/BUILD.gn
+++ b/chromeos/services/ime/public/mojom/BUILD.gn
@@ -14,6 +14,7 @@
 
   public_deps = [
     "//mojo/public/mojom/base",
+    "//sandbox/policy/mojom",
     "//url/mojom:url_mojom_gurl",
   ]
 
diff --git a/chromeos/services/ime/public/mojom/ime_service.mojom b/chromeos/services/ime/public/mojom/ime_service.mojom
index 6a2accb..de53c37 100644
--- a/chromeos/services/ime/public/mojom/ime_service.mojom
+++ b/chromeos/services/ime/public/mojom/ime_service.mojom
@@ -9,6 +9,7 @@
 import "chromeos/services/ime/public/mojom/input_engine.mojom";
 import "chromeos/services/ime/public/mojom/input_method.mojom";
 import "chromeos/services/ime/public/mojom/input_method_host.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 
 // IME on ChromeOS consists of three parts:
 // - The IME running in an extension to provide a soft keyboard
@@ -65,6 +66,7 @@
 // process to handle IME related operations. There are two clients of the IME
 // service: the browser process for certain first-party input methods and the
 // renderer process running the first-party IME extension.
+[ServiceSandbox=sandbox.mojom.Sandbox.kIme]
 interface ImeService {
   // Injects a remote PlatformAccessProvider interface that the service can use
   // to request privileged operations from the client (i.e. the browser).
diff --git a/chromeos/services/libassistant/public/mojom/BUILD.gn b/chromeos/services/libassistant/public/mojom/BUILD.gn
index 5884e73..eb7ca9d3 100644
--- a/chromeos/services/libassistant/public/mojom/BUILD.gn
+++ b/chromeos/services/libassistant/public/mojom/BUILD.gn
@@ -28,6 +28,8 @@
     "timer_controller.mojom",
   ]
 
+  public_deps = [ "//sandbox/policy/mojom" ]
+
   deps = [
     "//ash/public/mojom",
     "//chromeos/services/assistant/public/mojom",
diff --git a/chromeos/services/libassistant/public/mojom/service.mojom b/chromeos/services/libassistant/public/mojom/service.mojom
index 9532bbe5..8ac90c0 100644
--- a/chromeos/services/libassistant/public/mojom/service.mojom
+++ b/chromeos/services/libassistant/public/mojom/service.mojom
@@ -18,6 +18,7 @@
 import "chromeos/services/libassistant/public/mojom/speech_recognition_observer.mojom";
 import "chromeos/services/libassistant/public/mojom/timer_controller.mojom";
 import "chromeos/services/libassistant/public/mojom/notification_delegate.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 
 // The main interface to the Libassistant service on ChromeOS.
 // Libassistant provides access to the Google Assistant.
@@ -25,6 +26,7 @@
 // all Libassistant calls to this service.
 // It is used by //chromeos/services/assistant/proxy, which is a trusted
 // service running in the browser process.
+[ServiceSandbox=sandbox.mojom.Sandbox.kLibassistant]
 interface LibassistantService {
 
   // Bind everything needed to start the service.
diff --git a/chromeos/services/tts/public/mojom/BUILD.gn b/chromeos/services/tts/public/mojom/BUILD.gn
index bb15158..014d49e 100644
--- a/chromeos/services/tts/public/mojom/BUILD.gn
+++ b/chromeos/services/tts/public/mojom/BUILD.gn
@@ -7,5 +7,8 @@
 mojom("mojom") {
   sources = [ "tts_service.mojom" ]
 
-  public_deps = [ "//media/mojo/mojom" ]
+  public_deps = [
+    "//media/mojo/mojom",
+    "//sandbox/policy/mojom",
+  ]
 }
diff --git a/chromeos/services/tts/public/mojom/tts_service.mojom b/chromeos/services/tts/public/mojom/tts_service.mojom
index f51e4f38b0..a54a4ca4 100644
--- a/chromeos/services/tts/public/mojom/tts_service.mojom
+++ b/chromeos/services/tts/public/mojom/tts_service.mojom
@@ -5,6 +5,7 @@
 module chromeos.tts.mojom;
 
 import "media/mojo/mojom/audio_stream_factory.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 
 // Audio parameters used for PlaybackTtsStream.
 struct AudioParameters {
@@ -16,6 +17,7 @@
 // tts-sandboxed process.  TtsEngineExtensionObserver, the other end of this
 // interface, in the browser process, brokers a connection between TtsService
 // and two possible engine types, [Google|Playback]TtsStream.
+[ServiceSandbox=sandbox.mojom.Sandbox.kTts]
 interface TtsService {
   // Binds a GoogleTtsStream received by this service.
   // The remote lives in the Google tts component extension.
diff --git a/components/autofill/core/browser/autofill_regex_constants.cc b/components/autofill/core/browser/autofill_regex_constants.cc
index 08271ba..ebe1e4f 100644
--- a/components/autofill/core/browser/autofill_regex_constants.cc
+++ b/components/autofill/core/browser/autofill_regex_constants.cc
@@ -316,7 +316,7 @@
     u"|メールアドレス"               // ja-JP
     u"|Электронн(ая|ой).?Почт(а|ы)"  // ru
     u"|邮件|邮箱"                    // zh-CN
-    u"|電郵地址"                     // zh-TW
+    u"|電郵地址|電子信箱"            // zh-TW
     u"|ഇ-മെയില്‍|ഇലക്ട്രോണിക്.?"
     u"മെയിൽ"                                        // ml
     u"|ایمیل|پست.*الکترونیک"                        // fa
diff --git a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
index df9ccf22..906b9aa 100644
--- a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
+++ b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
@@ -2121,7 +2121,7 @@
     "zh-TW": [
       {
         "pattern_identifier": "zh_email_preserving",
-        "positive_pattern": "電郵地址",
+        "positive_pattern": "電郵地址|電子信箱",
         "positive_score": 1.4,
         "negative_pattern": null,
         "match_field_attributes": 3,
diff --git a/components/browsing_data/core/browsing_data_utils_unittest.cc b/components/browsing_data/core/browsing_data_utils_unittest.cc
index d6021d53..338afb7 100644
--- a/components/browsing_data/core/browsing_data_utils_unittest.cc
+++ b/components/browsing_data/core/browsing_data_utils_unittest.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/callback_helpers.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
@@ -55,8 +56,7 @@
 
 // Tests the complex output of the Autofill counter.
 TEST_F(BrowsingDataUtilsTest, AutofillCounterResult) {
-  AutofillCounter counter(
-      scoped_refptr<FakeWebDataService>(new FakeWebDataService()), nullptr);
+  AutofillCounter counter(base::MakeRefCounted<FakeWebDataService>(), nullptr);
 
   // Test all configurations of zero and nonzero partial results for datatypes.
   // Test singular and plural for each datatype.
@@ -101,8 +101,7 @@
 
 // Tests the output of the Passwords counter.
 TEST_F(BrowsingDataUtilsTest, PasswordsCounterResult) {
-  scoped_refptr<password_manager::TestPasswordStore> store(
-      new password_manager::TestPasswordStore());
+  auto store = base::MakeRefCounted<password_manager::TestPasswordStore>();
   PasswordsCounter counter(
       scoped_refptr<password_manager::PasswordStore>(store), nullptr, nullptr);
 
diff --git a/components/cdm/renderer/widevine_key_system_properties.cc b/components/cdm/renderer/widevine_key_system_properties.cc
index f0b2812..05f6d81 100644
--- a/components/cdm/renderer/widevine_key_system_properties.cc
+++ b/components/cdm/renderer/widevine_key_system_properties.cc
@@ -4,6 +4,7 @@
 
 #include "components/cdm/renderer/widevine_key_system_properties.h"
 
+#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/feature_list.h"
 #include "build/build_config.h"
@@ -144,12 +145,19 @@
 #if defined(OS_CHROMEOS)
   // Hardware security requires HWDRM or remote attestation, both of these
   // require an identifier.
-  if (robustness >= Robustness::HW_SECURE_CRYPTO || hw_secure_codecs_required)
+  if (robustness >= Robustness::HW_SECURE_CRYPTO || hw_secure_codecs_required) {
 #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kLacrosUseChromeosProtectedMedia)) {
+      return EmeConfigRule::IDENTIFIER_REQUIRED;
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
     return EmeConfigRule::IDENTIFIER_AND_HW_SECURE_CODECS_REQUIRED;
 #else
     return EmeConfigRule::IDENTIFIER_REQUIRED;
 #endif
+  }
 
   // For video, recommend remote attestation if HW_SECURE_ALL is available,
   // regardless of the value of |robustness|, because it enables hardware
diff --git a/components/download/network/network_status_listener.h b/components/download/network/network_status_listener.h
index c4d68d3..a6d973d 100644
--- a/components/download/network/network_status_listener.h
+++ b/components/download/network/network_status_listener.h
@@ -47,7 +47,8 @@
  protected:
   NetworkStatusListener();
 
-  // The only observer that listens to connection type change.
+  // The only observer that listens to connection type change. Must outlive this
+  // class.
   Observer* observer_ = nullptr;
 
   // The current network status.
diff --git a/components/download/network/network_status_listener_impl.cc b/components/download/network/network_status_listener_impl.cc
index bcc011a..34674b1 100644
--- a/components/download/network/network_status_listener_impl.cc
+++ b/components/download/network/network_status_listener_impl.cc
@@ -21,7 +21,7 @@
   bool sync = network_connection_tracker_->GetConnectionType(
       &connection_type_,
       base::BindOnce(&NetworkStatusListenerImpl::OnNetworkStatusReady,
-                     base::Unretained(this)));
+                     weak_ptr_factory_.GetWeakPtr()));
   if (sync)
     observer_->OnNetworkStatusReady(connection_type_);
 }
diff --git a/components/download/network/network_status_listener_impl.h b/components/download/network/network_status_listener_impl.h
index 7aad4813..69b9473 100644
--- a/components/download/network/network_status_listener_impl.h
+++ b/components/download/network/network_status_listener_impl.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_DOWNLOAD_NETWORK_NETWORK_STATUS_LISTENER_IMPL_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "components/download/network/network_status_listener.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 
@@ -34,6 +35,7 @@
 
   network::NetworkConnectionTracker* network_connection_tracker_;
 
+  base::WeakPtrFactory<NetworkStatusListenerImpl> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(NetworkStatusListenerImpl);
 };
 
diff --git a/components/management_strings.grdp b/components/management_strings.grdp
index c9fb9eb..82d9120 100644
--- a/components/management_strings.grdp
+++ b/components/management_strings.grdp
@@ -47,12 +47,22 @@
 
   <!-- Browser managed status section -->
   <if expr="not chromeos">
-    <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the browser is managed, it indicates what the administrator can do on the browser.">
+    <if expr="_google_chrome">
+      <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the (Google-branded) Chrome browser is managed, it indicates what the administrator can do on the browser.">
       Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
-    </message>
-    <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the browser is not managed">
-      This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
-    </message>
+      </message>
+      <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the (Google-branded) Chrome browser is not managed">
+       This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+      </message>
+    </if>
+    <if expr="not _google_chrome">
+      <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the (non-Google-branded) Chromium browser is managed, it indicates what the administrator can do on the browser.">
+      Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chromium. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+      </message>
+      <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the (non-Google-branded) Chromium browser is not managed">
+       This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chromium. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+      </message>
+    </if>
   </if>
 
   <!-- Chrome OS managed status section -->
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1
index e88b7c0..2ea1997 100644
--- a/components/management_strings_grdp/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_BROWSER_NOTICE.png.sha1
@@ -1 +1 @@
-fe07e19a701b3e8ee95eb5062063ac53cfdbe4f3
\ No newline at end of file
+ad0a9ae496e2d8a74660cd371bcc9acc79b475a2
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_NOT_MANAGED_NOTICE.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_NOT_MANAGED_NOTICE.png.sha1
index e88b7c0..20ed25a 100644
--- a/components/management_strings_grdp/IDS_MANAGEMENT_NOT_MANAGED_NOTICE.png.sha1
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_NOT_MANAGED_NOTICE.png.sha1
@@ -1 +1 @@
-fe07e19a701b3e8ee95eb5062063ac53cfdbe4f3
\ No newline at end of file
+1a56c5e54aeb312486f7f3b4564cc6f64525aac3
\ No newline at end of file
diff --git a/components/media_router/browser/media_router_metrics.cc b/components/media_router/browser/media_router_metrics.cc
index 25b1512..95337e7 100644
--- a/components/media_router/browser/media_router_metrics.cc
+++ b/components/media_router/browser/media_router_metrics.cc
@@ -9,8 +9,10 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/time/default_clock.h"
+#include "components/media_router/common/media_route_provider_helper.h"
 #include "components/media_router/common/media_sink.h"
 #include "components/media_router/common/media_source.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
@@ -49,6 +51,24 @@
   }
 }
 
+std::string GetDeviceCountHistogramName(const std::string& ui,
+                                        MediaRouterDialogOpenOrigin origin,
+                                        mojom::MediaRouteProviderId provider,
+                                        bool is_available) {
+  std::string trigger;
+  switch (origin) {
+    case MediaRouterDialogOpenOrigin::PAGE:
+      trigger = "PresentationApi";
+      break;
+    default:
+      trigger = "BrowserUi";
+  }
+  std::string mrp = ProviderIdToString(provider);
+  std::string state = is_available ? "Available" : "Unavailable";
+  return base::StrCat({MediaRouterMetrics::kHistogramUiDeviceCount, ".", ui,
+                       ".", trigger, ".", mrp, ".", state});
+}
+
 PresentationUrlType GetPresentationUrlType(const GURL& url) {
   if (url.SchemeIs(kDialPresentationUrlScheme))
     return PresentationUrlType::kDial;
@@ -114,6 +134,10 @@
     "MediaRouter.Ui.IconStateAtInit";
 
 // static
+const base::TimeDelta MediaRouterMetrics::kDeviceCountMetricDelay =
+    base::TimeDelta::FromSeconds(3);
+
+// static
 void MediaRouterMetrics::RecordMediaRouterDialogOrigin(
     MediaRouterDialogOpenOrigin origin) {
   DCHECK_LT(static_cast<int>(origin),
@@ -192,6 +216,30 @@
 }
 
 // static
+void MediaRouterMetrics::RecordGmcDeviceCount(
+    MediaRouterDialogOpenOrigin origin,
+    mojom::MediaRouteProviderId provider,
+    bool is_available,
+    int count) {
+  base::UmaHistogramCounts100(
+      GetDeviceCountHistogramName("GlobalMediaControls", origin, provider,
+                                  is_available),
+      count);
+}
+
+// static
+void MediaRouterMetrics::RecordCastDialogDeviceCount(
+    MediaRouterDialogOpenOrigin origin,
+    mojom::MediaRouteProviderId provider,
+    bool is_available,
+    int count) {
+  base::UmaHistogramCounts100(
+      GetDeviceCountHistogramName("CastHarmony", origin, provider,
+                                  is_available),
+      count);
+}
+
+// static
 void MediaRouterMetrics::RecordStartRouteDeviceIndex(int index) {
   base::UmaHistogramSparse(kHistogramStartLocalPosition, std::min(index, 100));
 }
diff --git a/components/media_router/browser/media_router_metrics.h b/components/media_router/browser/media_router_metrics.h
index 0a3c590..5cf0a48 100644
--- a/components/media_router/browser/media_router_metrics.h
+++ b/components/media_router/browser/media_router_metrics.h
@@ -68,9 +68,10 @@
   CONTEXTUAL_MENU = 2,
   PAGE = 3,
   APP_MENU = 4,
+  SYSTEM_TRAY = 5,
 
   // NOTE: Add entries only immediately above this line.
-  TOTAL_COUNT = 5
+  TOTAL_COUNT = 6
 };
 
 // The possible outcomes from a route creation response.
@@ -139,6 +140,10 @@
   static const char kHistogramUiFirstAction[];
   static const char kHistogramUiIconStateAtInit[];
 
+  // When recording the number of devices shown in UI we record after a delay
+  // because discovering devices can take some time after the UI is shown.
+  static const base::TimeDelta kDeviceCountMetricDelay;
+
   // Records where the user clicked to open the Media Router dialog.
   static void RecordMediaRouterDialogOrigin(MediaRouterDialogOpenOrigin origin);
 
@@ -180,6 +185,17 @@
   // may be 0.
   static void RecordDeviceCount(int device_count);
 
+  // Records the number of sinks in |is_available| state, provided by |provider|
+  // that was opened via |origin|.
+  static void RecordGmcDeviceCount(MediaRouterDialogOpenOrigin origin,
+                                   mojom::MediaRouteProviderId provider,
+                                   bool is_available,
+                                   int count);
+  static void RecordCastDialogDeviceCount(MediaRouterDialogOpenOrigin origin,
+                                          mojom::MediaRouteProviderId provider,
+                                          bool is_available,
+                                          int count);
+
   // Records the index of the device the user has started casting to on the
   // devices list. The index starts at 0.
   static void RecordStartRouteDeviceIndex(int index);
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 57b0438..6f643acbd5 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1302,6 +1302,16 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Use New Tab Page as homepage',
+        },
+        {
+          'value': False,
+          'caption': 'Do not use New Tab Page as homepage',
+        },
+      ],
       'example_value': True,
       'id': 2,
       'caption': '''Use New Tab Page as homepage''',
@@ -1349,6 +1359,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable the default browser check on startup',
+        },
+        {
+          'value': False,
+          'caption': 'Disable the default browser check on startup',
+        },
+      ],
       'example_value': True,
       'id': 3,
       'caption': '''Set <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> as Default Browser''',
@@ -1398,6 +1418,16 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable alternate error pages',
+        },
+        {
+          'value': False,
+          'caption': 'Disable alternate error pages',
+        },
+      ],
       'example_value': True,
       'id': 5,
       'caption': '''Enable alternate error pages''',
@@ -1422,6 +1452,16 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable search suggestions',
+        },
+        {
+          'value': False,
+          'caption': 'Disable search suggestions',
+        },
+      ],
       'example_value': True,
       'id': 6,
       'caption': '''Enable search suggestions''',
@@ -1443,6 +1483,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Detect covered window and suspend its painting',
+        },
+        {
+          'value': False,
+          'caption': 'Do not detect covered window',
+        },
+      ],
       'deprecated': True,
       'example_value': True,
       'id': 675,
@@ -1653,6 +1703,16 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable globally scoped HTTP authentication cache',
+        },
+        {
+          'value': False,
+          'caption': 'Disable globally scoped HTTP authentication cache',
+        },
+      ],
       'example_value': False,
       'id': 643,
       'caption': '''Enable globally scoped HTTP auth cache''',
@@ -1756,6 +1816,16 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable HTTP/0.9 support on non-default ports',
+        },
+        {
+          'value': False,
+          'caption': 'Disable HTTP/0.9 support on non-default ports',
+        },
+      ],
       'example_value': False,
       'id': 345,
       'caption': '''Enable HTTP/0.9 support on non-default ports''',
diff --git a/components/renderer_context_menu/context_menu_content_type.cc b/components/renderer_context_menu/context_menu_content_type.cc
index 93376436..8c13b5f 100644
--- a/components/renderer_context_menu/context_menu_content_type.cc
+++ b/components/renderer_context_menu/context_menu_content_type.cc
@@ -160,7 +160,8 @@
              blink::mojom::ContextMenuDataInputFieldType::kPassword;
 
     case ITEM_GROUP_LENS_REGION_SEARCH:
-      return true;
+      // Region Search is only enabled when image options are not.
+      return !SupportsGroupInternal(ITEM_GROUP_SEARCHWEBFORIMAGE);
 
     default:
       NOTREACHED();
diff --git a/components/resources/components_scaled_resources.grd b/components/resources/components_scaled_resources.grd
index b404cac..940244d 100644
--- a/components/resources/components_scaled_resources.grd
+++ b/components/resources/components_scaled_resources.grd
@@ -22,7 +22,6 @@
       <!-- Generic resources -->
       <if expr="not is_android">
         <structure type="chrome_scaled_image" name="IDR_HISTORY_FAVICON" file="favicon_history.png" />
-        <structure type="chrome_scaled_image" name="IDR_INFO_FAVICON" file="favicon_info.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_SAD_WEBVIEW" file="webview-crash.png" />
       <structure type="chrome_scaled_image" name="IDR_SAD_PLUGIN" file="sadplugin.png" />
diff --git a/components/resources/default_100_percent/favicon_info.png b/components/resources/default_100_percent/favicon_info.png
deleted file mode 100644
index bdd0358..0000000
--- a/components/resources/default_100_percent/favicon_info.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/favicon_info.png b/components/resources/default_200_percent/favicon_info.png
deleted file mode 100644
index 07f97a6..0000000
--- a/components/resources/default_200_percent/favicon_info.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/favicon_info.png b/components/resources/default_300_percent/favicon_info.png
deleted file mode 100644
index fc1108a..0000000
--- a/components/resources/default_300_percent/favicon_info.png
+++ /dev/null
Binary files differ
diff --git a/components/test/data/history/README.md b/components/test/data/history/README.md
index 1acdc663..bbd58412 100644
--- a/components/test/data/history/README.md
+++ b/components/test/data/history/README.md
@@ -4,7 +4,7 @@
 
 1. Build the `sqlite_shell` target. This will build the [SQLite CLI].
 
-        $ ninja sqlite_shell
+        $ ninja -C out/Debug/ sqlite_shell
 
 2. Run Chrome/Chromium with a fresh profile directory and immediately quit. It
    doesn't really matter how long you run it, but there'll be less work for you
@@ -12,14 +12,17 @@
 
         $ out/Debug/chrome-wrapper --user-data-dir=foo
 
-3. Locate the `History` file in the profile directory.
+3. Locate the `History` file in the user-data-dir directory; e.g., `foo/Default/History`.
 
 4. Dump the `History` database into a text file:
 
-        $ echo '.dump' | sqlite_shell foo/Default/History > history.sql
+        $ echo '.dump' | out/Debug/sqlite_shell foo/Default/History > history.sql
 
 5. Manually remove all `INSERT INTO` statements other than the statements
    populating the `meta` table.
 
+To generate history.N.sql, be sure to run these steps before implementing the N+1 migration in 
+chromium. I.e. you'll usually want to run these steps on the main branch rather than the CL branch.
+
 [SQLite CLI]: https://www.sqlite.org/cli.html
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 6b5ecc1..d13c610 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -757,6 +757,8 @@
     "devtools/devtools_video_consumer.h",
     "devtools/forwarding_agent_host.cc",
     "devtools/forwarding_agent_host.h",
+    "devtools/frame_auto_attacher.cc",
+    "devtools/frame_auto_attacher.h",
     "devtools/network_service_devtools_observer.cc",
     "devtools/network_service_devtools_observer.h",
     "devtools/protocol/audits_handler.cc",
diff --git a/content/browser/devtools/frame_auto_attacher.cc b/content/browser/devtools/frame_auto_attacher.cc
new file mode 100644
index 0000000..49299584
--- /dev/null
+++ b/content/browser/devtools/frame_auto_attacher.cc
@@ -0,0 +1,267 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/frame_auto_attacher.h"
+
+#include "content/browser/devtools/devtools_renderer_channel.h"
+#include "content/browser/devtools/render_frame_devtools_agent_host.h"
+#include "content/browser/devtools/service_worker_devtools_agent_host.h"
+#include "content/browser/renderer_host/frame_tree.h"
+#include "content/browser/renderer_host/navigation_request.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+
+namespace content {
+
+namespace {
+
+using ScopeAgentsMap =
+    std::map<GURL, std::unique_ptr<ServiceWorkerDevToolsAgentHost::List>>;
+
+void GetMatchingHostsByScopeMap(
+    const ServiceWorkerDevToolsAgentHost::List& agent_hosts,
+    const base::flat_set<GURL>& urls,
+    ScopeAgentsMap* scope_agents_map) {
+  base::flat_set<GURL> host_name_set;
+  for (const GURL& url : urls)
+    host_name_set.insert(url.GetOrigin());
+  for (const auto& host : agent_hosts) {
+    if (host_name_set.find(host->scope().GetOrigin()) == host_name_set.end())
+      continue;
+    const auto& it = scope_agents_map->find(host->scope());
+    if (it == scope_agents_map->end()) {
+      std::unique_ptr<ServiceWorkerDevToolsAgentHost::List> new_list(
+          new ServiceWorkerDevToolsAgentHost::List());
+      new_list->push_back(host);
+      (*scope_agents_map)[host->scope()] = std::move(new_list);
+    } else {
+      it->second->push_back(host);
+    }
+  }
+}
+
+void AddEligibleHosts(const ServiceWorkerDevToolsAgentHost::List& list,
+                      ServiceWorkerDevToolsAgentHost::Map* result) {
+  base::Time last_installed_time;
+  base::Time last_doomed_time;
+  for (const auto& host : list) {
+    if (host->version_installed_time() > last_installed_time)
+      last_installed_time = host->version_installed_time();
+    if (host->version_doomed_time() > last_doomed_time)
+      last_doomed_time = host->version_doomed_time();
+  }
+  for (const auto& host : list) {
+    // We don't attech old redundant Service Workers when there is newer
+    // installed Service Worker.
+    if (host->version_doomed_time().is_null() ||
+        (last_installed_time < last_doomed_time &&
+         last_doomed_time == host->version_doomed_time())) {
+      (*result)[host->GetId()] = host;
+    }
+  }
+}
+
+ServiceWorkerDevToolsAgentHost::Map GetMatchingServiceWorkers(
+    BrowserContext* browser_context,
+    const base::flat_set<GURL>& urls) {
+  ServiceWorkerDevToolsAgentHost::Map result;
+  if (!browser_context)
+    return result;
+
+  ServiceWorkerDevToolsAgentHost::List agent_hosts;
+  ServiceWorkerDevToolsManager::GetInstance()
+      ->AddAllAgentHostsForBrowserContext(browser_context, &agent_hosts);
+
+  ScopeAgentsMap scope_agents_map;
+  GetMatchingHostsByScopeMap(agent_hosts, urls, &scope_agents_map);
+
+  for (const auto& it : scope_agents_map)
+    AddEligibleHosts(*it.second.get(), &result);
+
+  return result;
+}
+
+base::flat_set<GURL> GetFrameUrls(RenderFrameHostImpl* render_frame_host) {
+  // We try to attach to a service worker in the following cases:
+  // 1. SW is created while user is inspecting frame (from WorkerCreated).
+  // 2. Frame has navigated and we are picking up new SW corresponding to new
+  //    url (from DidFinishNavigation).
+  // 3. Frame is trying to navigate and it spawns a new SW which we pick up
+  //    (from WorkerCreated). See also https://crbug.com/907072
+  //
+  // We are not attaching in the following case:
+  // 4. Frame is trying to navigate and we _should_ pick up an existing SW but
+  //    we don't. We _could_ do this, but since we are not pausing the
+  //    navigation, there is no principal difference between picking up SW
+  //    earlier or later.
+  //
+  // We also try to detach from SW picked up for [3] if navigation has failed
+  // (from DidFinishNavigation).
+
+  base::flat_set<GURL> frame_urls;
+  if (render_frame_host) {
+    for (FrameTreeNode* node : render_frame_host->frame_tree()->Nodes()) {
+      frame_urls.insert(node->current_url());
+      // We use both old and new frame urls to support [3], where we attach
+      // while navigation is still ongoing.
+      if (node->navigation_request()) {
+        frame_urls.insert(node->navigation_request()->common_params().url);
+      }
+    }
+  }
+  return frame_urls;
+}
+
+}  // namespace
+
+FrameAutoAttacher::FrameAutoAttacher(DevToolsRendererChannel* renderer_channel)
+    : RendererAutoAttacherBase(renderer_channel) {}
+
+FrameAutoAttacher::~FrameAutoAttacher() = default;
+
+void FrameAutoAttacher::SetRenderFrameHost(
+    RenderFrameHostImpl* render_frame_host) {
+  render_frame_host_ = render_frame_host;
+  if (!auto_attach())
+    return;
+  UpdateFrames();
+  UpdatePortals();
+  ReattachServiceWorkers();
+}
+
+void FrameAutoAttacher::DidFinishNavigation(
+    NavigationRequest* navigation_request) {
+  if (!render_frame_host_)
+    return;
+
+  if (navigation_request->frame_tree_node() ==
+      render_frame_host_->frame_tree_node()) {
+    ReattachServiceWorkers();
+    return;
+  }
+
+  // We only care about subframes that have |render_frame_host_| as their
+  // local root.
+  if (!navigation_request->HasCommitted())
+    return;
+  RenderFrameHostImpl* parent = navigation_request->GetParentFrame();
+  while (parent && !parent->is_local_root())
+    parent = parent->GetParent();
+  if (parent != render_frame_host_)
+    return;
+
+  // Some subframes may not be attached through
+  // TargetHandler::ResponseThrottle because DevTools wasn't attached when the
+  // navigation started, so no throttle was installed. We auto-attach them
+  // here instead (note that we cannot honor |wait_for_debugger_on_start_| in
+  // this case).
+  AutoAttachToFrame(navigation_request, false);
+}
+
+void FrameAutoAttacher::UpdatePortals() {
+  if (!auto_attach())
+    return;
+
+  Hosts new_hosts;
+  if (render_frame_host_ &&
+      render_frame_host_->frame_tree_node()->IsMainFrame()) {
+    WebContentsImpl* outer_web_contents = static_cast<WebContentsImpl*>(
+        WebContents::FromRenderFrameHost(render_frame_host_));
+    for (WebContents* web_contents :
+         outer_web_contents->GetInnerWebContents()) {
+      WebContentsImpl* web_contents_impl =
+          static_cast<WebContentsImpl*>(web_contents);
+      if (!web_contents_impl->IsPortal())
+        continue;
+
+      scoped_refptr<DevToolsAgentHost> new_host =
+          RenderFrameDevToolsAgentHost::GetOrCreateFor(
+              web_contents_impl->GetFrameTree()->root());
+      new_hosts.insert(new_host);
+    }
+  }
+
+  DispatchSetAttachedTargetsOfType(new_hosts, DevToolsAgentHost::kTypePage);
+}
+
+void FrameAutoAttacher::UpdateAutoAttach(base::OnceClosure callback) {
+  if (auto_attach()) {
+    if (render_frame_host_ && !render_frame_host_->GetParent() &&
+        !observing_service_workers_) {
+      observing_service_workers_ = true;
+      ServiceWorkerDevToolsManager::GetInstance()->AddObserver(this);
+      ReattachServiceWorkers();
+      UpdateFrames();
+      UpdatePortals();
+    }
+  } else if (observing_service_workers_) {
+    ServiceWorkerDevToolsManager::GetInstance()->RemoveObserver(this);
+    observing_service_workers_ = false;
+  }
+  RendererAutoAttacherBase::UpdateAutoAttach(std::move(callback));
+}
+
+void FrameAutoAttacher::WorkerCreated(ServiceWorkerDevToolsAgentHost* host,
+                                      bool* should_pause_on_start) {
+  if (!render_frame_host_)
+    return;
+  BrowserContext* browser_context =
+      render_frame_host_->GetProcess()->GetBrowserContext();
+  auto hosts = GetMatchingServiceWorkers(browser_context,
+                                         GetFrameUrls(render_frame_host_));
+  if (hosts.find(host->GetId()) == hosts.end())
+    return;
+
+  *should_pause_on_start = wait_for_debugger_on_start();
+  DispatchAutoAttach(host, *should_pause_on_start);
+}
+
+void FrameAutoAttacher::WorkerDestroyed(ServiceWorkerDevToolsAgentHost* host) {
+  ReattachServiceWorkers();
+}
+
+void FrameAutoAttacher::ReattachServiceWorkers() {
+  if (!observing_service_workers_ || !render_frame_host_)
+    return;
+  BrowserContext* browser_context =
+      render_frame_host_->GetProcess()->GetBrowserContext();
+  auto matching = GetMatchingServiceWorkers(browser_context,
+                                            GetFrameUrls(render_frame_host_));
+  Hosts new_hosts;
+  for (const auto& pair : matching)
+    new_hosts.insert(pair.second);
+  DispatchSetAttachedTargetsOfType(new_hosts,
+                                   DevToolsAgentHost::kTypeServiceWorker);
+}
+
+void FrameAutoAttacher::UpdateFrames() {
+  DCHECK(auto_attach());
+
+  Hosts new_hosts;
+  if (render_frame_host_) {
+    base::queue<FrameTreeNode*> queue;
+    for (size_t i = 0; i < render_frame_host_->child_count(); ++i) {
+      queue.push(render_frame_host_->child_at(i));
+    }
+    while (!queue.empty()) {
+      FrameTreeNode* node = queue.front();
+      queue.pop();
+      bool should_create = node->current_frame_host()->is_local_root_subframe();
+      if (should_create) {
+        scoped_refptr<DevToolsAgentHost> new_host =
+            RenderFrameDevToolsAgentHost::GetOrCreateFor(node);
+        new_hosts.insert(new_host);
+        // Note: We don't add children of a local root to |queue|, as they
+        // will be looked at by a separate TargetAutoAttacher created for the
+        // local root.
+      } else {
+        for (size_t i = 0; i < node->child_count(); ++i)
+          queue.push(node->child_at(i));
+      }
+    }
+  }
+
+  DispatchSetAttachedTargetsOfType(new_hosts, DevToolsAgentHost::kTypeFrame);
+}
+
+}  // namespace content
diff --git a/content/browser/devtools/frame_auto_attacher.h b/content/browser/devtools/frame_auto_attacher.h
new file mode 100644
index 0000000..2df2e52
--- /dev/null
+++ b/content/browser/devtools/frame_auto_attacher.h
@@ -0,0 +1,47 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef CONTENT_BROWSER_DEVTOOLS_FRAME_AUTO_ATTACHER_H_
+#define CONTENT_BROWSER_DEVTOOLS_FRAME_AUTO_ATTACHER_H_
+
+#include "base/callback.h"
+#include "content/browser/devtools/protocol/target_auto_attacher.h"
+#include "content/browser/devtools/service_worker_devtools_manager.h"
+
+namespace content {
+
+class DevToolsRendererChannel;
+class NavigationRequest;
+class RenderFrameHostImpl;
+class ServiceWorkerDevToolsAgentHost;
+
+class FrameAutoAttacher : public protocol::RendererAutoAttacherBase,
+                          public ServiceWorkerDevToolsManager::Observer {
+ public:
+  explicit FrameAutoAttacher(DevToolsRendererChannel* renderer_channel);
+  ~FrameAutoAttacher() override;
+
+  void SetRenderFrameHost(RenderFrameHostImpl* render_frame_host);
+  void DidFinishNavigation(NavigationRequest* navigation_request);
+  void UpdatePortals();
+
+ protected:
+  // Base overrides.
+  void UpdateAutoAttach(base::OnceClosure callback) override;
+
+  // ServiceWorkerDevToolsManager::Observer implementation.
+  void WorkerCreated(ServiceWorkerDevToolsAgentHost* host,
+                     bool* should_pause_on_start) override;
+  void WorkerDestroyed(ServiceWorkerDevToolsAgentHost* host) override;
+
+  void ReattachServiceWorkers();
+  void UpdateFrames();
+
+ private:
+  RenderFrameHostImpl* render_frame_host_ = nullptr;
+  bool observing_service_workers_ = false;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DEVTOOLS_FRAME_AUTO_ATTACHER_H_
diff --git a/content/browser/devtools/protocol/target_auto_attacher.cc b/content/browser/devtools/protocol/target_auto_attacher.cc
index 72293ec..1510f3e5 100644
--- a/content/browser/devtools/protocol/target_auto_attacher.cc
+++ b/content/browser/devtools/protocol/target_auto_attacher.cc
@@ -4,12 +4,8 @@
 
 #include "content/browser/devtools/protocol/target_auto_attacher.h"
 
-#include "base/auto_reset.h"
-#include "base/containers/queue.h"
 #include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
-#include "content/browser/devtools/service_worker_devtools_agent_host.h"
-#include "content/browser/renderer_host/frame_tree.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 1ccc64a..02bd11d 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -22,6 +22,7 @@
 #include "content/browser/devtools/devtools_manager.h"
 #include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/browser/devtools/devtools_session.h"
+#include "content/browser/devtools/frame_auto_attacher.h"
 #include "content/browser/devtools/protocol/audits_handler.h"
 #include "content/browser/devtools/protocol/background_service_handler.h"
 #include "content/browser/devtools/protocol/browser_handler.h"
@@ -100,264 +101,8 @@
               ftn->current_frame_host()));
 }
 
-using ScopeAgentsMap =
-    std::map<GURL, std::unique_ptr<ServiceWorkerDevToolsAgentHost::List>>;
-
-void GetMatchingHostsByScopeMap(
-    const ServiceWorkerDevToolsAgentHost::List& agent_hosts,
-    const base::flat_set<GURL>& urls,
-    ScopeAgentsMap* scope_agents_map) {
-  base::flat_set<GURL> host_name_set;
-  for (const GURL& url : urls)
-    host_name_set.insert(url.GetOrigin());
-  for (const auto& host : agent_hosts) {
-    if (host_name_set.find(host->scope().GetOrigin()) == host_name_set.end())
-      continue;
-    const auto& it = scope_agents_map->find(host->scope());
-    if (it == scope_agents_map->end()) {
-      std::unique_ptr<ServiceWorkerDevToolsAgentHost::List> new_list(
-          new ServiceWorkerDevToolsAgentHost::List());
-      new_list->push_back(host);
-      (*scope_agents_map)[host->scope()] = std::move(new_list);
-    } else {
-      it->second->push_back(host);
-    }
-  }
-}
-
-void AddEligibleHosts(const ServiceWorkerDevToolsAgentHost::List& list,
-                      ServiceWorkerDevToolsAgentHost::Map* result) {
-  base::Time last_installed_time;
-  base::Time last_doomed_time;
-  for (const auto& host : list) {
-    if (host->version_installed_time() > last_installed_time)
-      last_installed_time = host->version_installed_time();
-    if (host->version_doomed_time() > last_doomed_time)
-      last_doomed_time = host->version_doomed_time();
-  }
-  for (const auto& host : list) {
-    // We don't attech old redundant Service Workers when there is newer
-    // installed Service Worker.
-    if (host->version_doomed_time().is_null() ||
-        (last_installed_time < last_doomed_time &&
-         last_doomed_time == host->version_doomed_time())) {
-      (*result)[host->GetId()] = host;
-    }
-  }
-}
-
-ServiceWorkerDevToolsAgentHost::Map GetMatchingServiceWorkers(
-    BrowserContext* browser_context,
-    const base::flat_set<GURL>& urls) {
-  ServiceWorkerDevToolsAgentHost::Map result;
-  if (!browser_context)
-    return result;
-
-  ServiceWorkerDevToolsAgentHost::List agent_hosts;
-  ServiceWorkerDevToolsManager::GetInstance()
-      ->AddAllAgentHostsForBrowserContext(browser_context, &agent_hosts);
-
-  ScopeAgentsMap scope_agents_map;
-  GetMatchingHostsByScopeMap(agent_hosts, urls, &scope_agents_map);
-
-  for (const auto& it : scope_agents_map)
-    AddEligibleHosts(*it.second.get(), &result);
-
-  return result;
-}
-
-base::flat_set<GURL> GetFrameUrls(RenderFrameHostImpl* render_frame_host) {
-  // We try to attach to a service worker in the following cases:
-  // 1. SW is created while user is inspecting frame (from WorkerCreated).
-  // 2. Frame has navigated and we are picking up new SW corresponding to new
-  //    url (from DidFinishNavigation).
-  // 3. Frame is trying to navigate and it spawns a new SW which we pick up
-  //    (from WorkerCreated). See also https://crbug.com/907072
-  //
-  // We are not attaching in the following case:
-  // 4. Frame is trying to navigate and we _should_ pick up an existing SW but
-  //    we don't. We _could_ do this, but since we are not pausing the
-  //    navigation, there is no principal difference between picking up SW
-  //    earlier or later.
-  //
-  // We also try to detach from SW picked up for [3] if navigation has failed
-  // (from DidFinishNavigation).
-
-  base::flat_set<GURL> frame_urls;
-  if (render_frame_host) {
-    for (FrameTreeNode* node : render_frame_host->frame_tree()->Nodes()) {
-      frame_urls.insert(node->current_url());
-      // We use both old and new frame urls to support [3], where we attach
-      // while navigation is still ongoing.
-      if (node->navigation_request()) {
-        frame_urls.insert(node->navigation_request()->common_params().url);
-      }
-    }
-  }
-  return frame_urls;
-}
-
 }  // namespace
 
-class RenderFrameDevToolsAgentHost::FrameAutoAttacher
-    : public protocol::RendererAutoAttacherBase,
-      public ServiceWorkerDevToolsManager::Observer {
- public:
-  explicit FrameAutoAttacher(DevToolsRendererChannel* renderer_channel)
-      : RendererAutoAttacherBase(renderer_channel) {}
-  ~FrameAutoAttacher() override = default;
-
-  void SetRenderFrameHost(RenderFrameHostImpl* render_frame_host) {
-    render_frame_host_ = render_frame_host;
-    if (!auto_attach())
-      return;
-    UpdateFrames();
-    UpdatePortals();
-    ReattachServiceWorkers();
-  }
-
-  void DidFinishNavigation(NavigationRequest* navigation_request) {
-    if (!render_frame_host_)
-      return;
-
-    if (navigation_request->frame_tree_node() ==
-        render_frame_host_->frame_tree_node()) {
-      ReattachServiceWorkers();
-      return;
-    }
-
-    // We only care about subframes that have |render_frame_host_| as their
-    // local root.
-    if (!navigation_request->HasCommitted())
-      return;
-    RenderFrameHostImpl* parent = navigation_request->GetParentFrame();
-    while (parent && !parent->is_local_root())
-      parent = parent->GetParent();
-    if (parent != render_frame_host_)
-      return;
-
-    // Some subframes may not be attached through
-    // TargetHandler::ResponseThrottle because DevTools wasn't attached when the
-    // navigation started, so no throttle was installed. We auto-attach them
-    // here instead (note that we cannot honor |wait_for_debugger_on_start_| in
-    // this case).
-    AutoAttachToFrame(navigation_request, false);
-  }
-
-  void UpdatePortals() {
-    if (!auto_attach())
-      return;
-
-    Hosts new_hosts;
-    if (render_frame_host_ &&
-        render_frame_host_->frame_tree_node()->IsMainFrame()) {
-      WebContentsImpl* outer_web_contents = static_cast<WebContentsImpl*>(
-          WebContents::FromRenderFrameHost(render_frame_host_));
-      for (WebContents* web_contents :
-           outer_web_contents->GetInnerWebContents()) {
-        WebContentsImpl* web_contents_impl =
-            static_cast<WebContentsImpl*>(web_contents);
-        if (!web_contents_impl->IsPortal())
-          continue;
-
-        scoped_refptr<DevToolsAgentHost> new_host =
-            RenderFrameDevToolsAgentHost::GetOrCreateFor(
-                web_contents_impl->GetFrameTree()->root());
-        new_hosts.insert(new_host);
-      }
-    }
-
-    DispatchSetAttachedTargetsOfType(new_hosts, DevToolsAgentHost::kTypePage);
-  }
-
- protected:
-  // ServiceWorkerDevToolsManager::Observer implementation.
-  void WorkerCreated(ServiceWorkerDevToolsAgentHost* host,
-                     bool* should_pause_on_start) override {
-    if (!render_frame_host_)
-      return;
-    BrowserContext* browser_context =
-        render_frame_host_->GetProcess()->GetBrowserContext();
-    auto hosts = GetMatchingServiceWorkers(browser_context,
-                                           GetFrameUrls(render_frame_host_));
-    if (hosts.find(host->GetId()) == hosts.end())
-      return;
-
-    *should_pause_on_start = wait_for_debugger_on_start();
-    DispatchAutoAttach(host, *should_pause_on_start);
-  }
-
-  void WorkerDestroyed(ServiceWorkerDevToolsAgentHost* host) override {
-    ReattachServiceWorkers();
-  }
-
-  void ReattachServiceWorkers() {
-    if (!observing_service_workers_ || !render_frame_host_)
-      return;
-    BrowserContext* browser_context =
-        render_frame_host_->GetProcess()->GetBrowserContext();
-    auto matching = GetMatchingServiceWorkers(browser_context,
-                                              GetFrameUrls(render_frame_host_));
-    Hosts new_hosts;
-    for (const auto& pair : matching)
-      new_hosts.insert(pair.second);
-    DispatchSetAttachedTargetsOfType(new_hosts,
-                                     DevToolsAgentHost::kTypeServiceWorker);
-  }
-
-  void UpdateAutoAttach(base::OnceClosure callback) override {
-    if (auto_attach()) {
-      if (render_frame_host_ && !render_frame_host_->GetParent() &&
-          !observing_service_workers_) {
-        observing_service_workers_ = true;
-        ServiceWorkerDevToolsManager::GetInstance()->AddObserver(this);
-        ReattachServiceWorkers();
-        UpdateFrames();
-        UpdatePortals();
-      }
-    } else if (observing_service_workers_) {
-      ServiceWorkerDevToolsManager::GetInstance()->RemoveObserver(this);
-      observing_service_workers_ = false;
-    }
-    RendererAutoAttacherBase::UpdateAutoAttach(std::move(callback));
-  }
-
-  void UpdateFrames() {
-    DCHECK(auto_attach());
-
-    Hosts new_hosts;
-    if (render_frame_host_) {
-      base::queue<FrameTreeNode*> queue;
-      for (size_t i = 0; i < render_frame_host_->child_count(); ++i) {
-        queue.push(render_frame_host_->child_at(i));
-      }
-      while (!queue.empty()) {
-        FrameTreeNode* node = queue.front();
-        queue.pop();
-        bool should_create =
-            node->current_frame_host()->is_local_root_subframe();
-        if (should_create) {
-          scoped_refptr<DevToolsAgentHost> new_host =
-              RenderFrameDevToolsAgentHost::GetOrCreateFor(node);
-          new_hosts.insert(new_host);
-          // Note: We don't add children of a local root to |queue|, as they
-          // will be looked at by a separate TargetAutoAttacher created for the
-          // local root.
-        } else {
-          for (size_t i = 0; i < node->child_count(); ++i)
-            queue.push(node->child_at(i));
-        }
-      }
-    }
-
-    DispatchSetAttachedTargetsOfType(new_hosts, DevToolsAgentHost::kTypeFrame);
-  }
-
- private:
-  RenderFrameHostImpl* render_frame_host_ = nullptr;
-  bool observing_service_workers_ = false;
-};
-
 FrameTreeNode* GetFrameTreeNodeAncestor(FrameTreeNode* frame_tree_node) {
   while (frame_tree_node && !ShouldCreateDevToolsForNode(frame_tree_node))
     frame_tree_node = FrameTreeNode::From(frame_tree_node->parent());
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 20dbd30..e9634fd 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -35,6 +35,7 @@
 class BrowserContext;
 class DevToolsFrameTraceRecorder;
 class FrameTreeNode;
+class FrameAutoAttacher;
 class NavigationRequest;
 class RenderFrameHostImpl;
 
@@ -113,7 +114,6 @@
 
  private:
   friend class DevToolsAgentHost;
-  class FrameAutoAttacher;
 
   static void UpdateRawHeadersAccess(RenderFrameHostImpl* rfh);
 
diff --git a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc
index 5bdaded..988dd00ed 100644
--- a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc
+++ b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc
@@ -8,6 +8,8 @@
 
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/numerics/safe_math.h"
@@ -26,7 +28,9 @@
 
 namespace {
 
-void ReadOnIOThread(storage::FileStreamReader* reader,
+void ReadOnIOThread(scoped_refptr<storage::FileSystemContext> context,
+                    storage::FileSystemURL url,
+                    uint64_t offset,
                     scoped_refptr<storage::BigIOBuffer> buffer,
                     scoped_refptr<base::SequencedTaskRunner> reply_runner,
                     base::OnceCallback<void(int)> callback) {
@@ -35,12 +39,24 @@
   auto wrapped_callback =
       base::BindPostTask(std::move(reply_runner), std::move(callback));
 
+  // The reader must be constructed on the IO thread.
+  std::unique_ptr<storage::FileStreamReader> reader =
+      context->CreateFileStreamReader(
+          url, offset, /*max_bytes_to_read=*/buffer->size(), base::Time());
+
   // Create two copies of |wrapped_callback| though it can still only be
   // called at most once. This is safe because Read() is guaranteed not to
   // call |wrapped_callback| if it returns synchronously.
   auto split_callback = base::SplitOnceCallback(std::move(wrapped_callback));
-  int rv = reader->Read(buffer.get(), buffer->size(),
-                        base::BindOnce(std::move(split_callback.first)));
+  // Keep the reader alive by binding it to its callback.
+  storage::FileStreamReader* reader_ptr = reader.get();
+  int rv = reader_ptr->Read(
+      buffer.get(), buffer->size(),
+      base::BindOnce(
+          [](std::unique_ptr<storage::FileStreamReader>,
+             base::OnceCallback<void(int)> callback,
+             int bytes_read) { std::move(callback).Run(bytes_read); },
+          std::move(reader), std::move(split_callback.first)));
   if (rv != net::ERR_IO_PENDING)
     std::move(split_callback.second).Run(rv);
 }
@@ -82,23 +98,17 @@
 
   auto buffer = base::MakeRefCounted<storage::BigIOBuffer>(max_bytes_to_read);
 
-  std::unique_ptr<storage::FileStreamReader> reader =
-      file_system_context()->CreateFileStreamReader(
-          url(), offset, /*max_bytes_to_read=*/buffer->size(), base::Time());
-
-  storage::FileStreamReader* reader_ptr = reader.get();
   content::GetIOThreadTaskRunner({})->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &ReadOnIOThread, reader_ptr, buffer,
-          base::SequencedTaskRunnerHandle::Get(),
+          &ReadOnIOThread, base::WrapRefCounted(file_system_context()), url(),
+          offset, buffer, base::SequencedTaskRunnerHandle::Get(),
           base::BindOnce(&FileSystemAccessFileDelegateHostImpl::DidRead,
-                         weak_factory_.GetWeakPtr(), std::move(reader), buffer,
+                         weak_factory_.GetWeakPtr(), buffer,
                          std::move(callback))));
 }
 
 void FileSystemAccessFileDelegateHostImpl::DidRead(
-    std::unique_ptr<storage::FileStreamReader> reader,
     scoped_refptr<storage::BigIOBuffer> buffer,
     ReadCallback callback,
     int rv) {
diff --git a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
index f9a21996..762f76bc 100644
--- a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
+++ b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
@@ -122,8 +122,7 @@
   }
   const storage::FileSystemURL& url() { return url_; }
 
-  void DidRead(std::unique_ptr<storage::FileStreamReader> reader,
-               scoped_refptr<storage::BigIOBuffer> buffer,
+  void DidRead(scoped_refptr<storage::BigIOBuffer> buffer,
                ReadCallback callback,
                int rv);
   void DidWrite(WriteState* state,
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index c95b2722..20c70c6 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -312,6 +312,11 @@
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
     switches::kHardwareVideoDecodeFrameRate,
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    switches::kLacrosEnablePlatformEncryptedHevc,
+    switches::kLacrosEnablePlatformHevc,
+    switches::kLacrosUseChromeosProtectedMedia,
+#endif
 };
 
 // These values are persisted to logs. Entries should not be renumbered and
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h
index 645feec..ce04233 100644
--- a/content/browser/interest_group/auction_runner.h
+++ b/content/browser/interest_group/auction_runner.h
@@ -14,7 +14,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "content/browser/interest_group/auction_process_manager.h"
-//#include "content/browser/interest_group/debuggable_worklet.h"
 #include "content/common/content_export.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom-forward.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
diff --git a/content/browser/media/key_system_support_impl.cc b/content/browser/media/key_system_support_impl.cc
index ab36b6b..680f6e1 100644
--- a/content/browser/media/key_system_support_impl.cc
+++ b/content/browser/media/key_system_support_impl.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_split.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "content/browser/media/cdm_registry_impl.h"
 #include "content/public/browser/cdm_registry.h"
 #include "content/public/browser/content_browser_client.h"
@@ -32,10 +33,6 @@
 #include "gpu/config/gpu_driver_bug_workaround_type.h"
 #endif
 
-#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-#include "ash/constants/ash_features.h"
-#endif  // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-
 namespace content {
 
 namespace {
@@ -127,7 +124,12 @@
     bool* lazy_initialize) {
   *lazy_initialize = false;
 
-#if !BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosUseChromeosProtectedMedia)) {
+    return absl::nullopt;
+  }
+#elif !BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
   if (!base::FeatureList::IsEnabled(media::kHardwareSecureDecryption)) {
     DVLOG(1) << "Hardware secure decryption disabled";
     return absl::nullopt;
diff --git a/content/browser/media/key_system_support_impl_unittest.cc b/content/browser/media/key_system_support_impl_unittest.cc
index 9ba6ae48..5a5684a 100644
--- a/content/browser/media/key_system_support_impl_unittest.cc
+++ b/content/browser/media/key_system_support_impl_unittest.cc
@@ -154,6 +154,26 @@
     return gpu_feature_info;
   }
 
+  void SelectHardwareSecureDecryption(bool enabled) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    if (enabled) {
+      base::CommandLine::ForCurrentProcess()->AppendSwitch(
+          switches::kLacrosUseChromeosProtectedMedia);
+    } else {
+      base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+          switches::kLacrosUseChromeosProtectedMedia);
+    }
+#else
+    if (enabled) {
+      scoped_feature_list_.InitAndEnableFeature(
+          media::kHardwareSecureDecryption);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          media::kHardwareSecureDecryption);
+    }
+#endif
+  }
+
   mojo::Remote<media::mojom::KeySystemSupport> key_system_support_;
   base::MockCallback<KeySystemSupportImpl::HardwareSecureCapabilityCB>
       hw_secure_capability_cb_;
@@ -184,14 +204,14 @@
 
 TEST_F(KeySystemSupportImplTest,
        HardwareSecureCapability_HardwareSecureDecryptionDisabled) {
-  scoped_feature_list_.InitAndDisableFeature(media::kHardwareSecureDecryption);
+  SelectHardwareSecureDecryption(false);
   Register("KeySystem", TestCdmCapability(), Robustness::kHardwareSecure);
 
   EXPECT_FALSE(IsSupported("KeySystem"));
 }
 
 TEST_F(KeySystemSupportImplTest, HardwareSecureCapability) {
-  scoped_feature_list_.InitAndEnableFeature(media::kHardwareSecureDecryption);
+  SelectHardwareSecureDecryption(true);
   Register("KeySystem", TestCdmCapability(), Robustness::kHardwareSecure);
 
   // Simulate GPU process initialization completing with GL unavailable.
@@ -246,7 +266,7 @@
 }
 
 TEST_F(KeySystemSupportImplTest, LazyInitialize_Supported) {
-  scoped_feature_list_.InitAndEnableFeature(media::kHardwareSecureDecryption);
+  SelectHardwareSecureDecryption(true);
   Register("KeySystem", absl::nullopt, Robustness::kHardwareSecure);
 
   // Simulate GPU process initialization completing with GL unavailable.
@@ -266,7 +286,7 @@
 }
 
 TEST_F(KeySystemSupportImplTest, LazyInitialize_NotSupported) {
-  scoped_feature_list_.InitAndEnableFeature(media::kHardwareSecureDecryption);
+  SelectHardwareSecureDecryption(true);
   Register("KeySystem", absl::nullopt, Robustness::kHardwareSecure);
 
   // Simulate GPU process initialization completing with GL unavailable.
@@ -287,7 +307,7 @@
 
 TEST_F(KeySystemSupportImplTest,
        LazyInitialize_HardwareSecureDecryptionDisabled) {
-  scoped_feature_list_.InitAndDisableFeature(media::kHardwareSecureDecryption);
+  SelectHardwareSecureDecryption(false);
   Register("KeySystem", absl::nullopt, Robustness::kHardwareSecure);
 
   EXPECT_FALSE(IsSupported("KeySystem"));
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index 7d246391..d15adcc 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -46,8 +46,6 @@
 #include "media/base/key_system_names.h"
 #include "media/mojo/mojom/cdm_service.mojom.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
 #if defined(OS_WIN) || BUILDFLAG(ENABLE_LIBRARY_CDMS)
@@ -81,7 +79,9 @@
   return cdm_name.size() <= kMaxCdmNameSize && base::IsStringASCII(cdm_name);
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+#if defined(OS_CHROMEOS)
 constexpr char kChromeOsCdmFileSystemId[] =
     "application_chromeos-cdm-factory-daemon";
 
@@ -96,9 +96,7 @@
 void ReportCdmTypeUMA(CrosCdmType cdm_type) {
   UMA_HISTOGRAM_ENUMERATION("Media.EME.CrosCdmType", cdm_type);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif  // defined(OS_CHROMEOS)
 
 // The amount of time to allow the secondary Media Service instance to idle
 // before tearing it down. Only used if the Content embedder defines how to
@@ -225,7 +223,7 @@
   DCHECK(render_frame_host_);
 
   std::string cdm_file_system_id;
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS) && BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
   // The file system ID passed in here is only used by the CDM obtained through
   // the |media_interface_factory_ptr_|.
   cdm_file_system_id = kChromeOsCdmFileSystemId;
@@ -366,8 +364,15 @@
       "CDM creation failed");
 
   // Handle `use_hw_secure_codecs` cases first.
-#if BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-  if (cdm_config.use_hw_secure_codecs &&
+#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  const bool enable_cdm_factory_daemon =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosUseChromeosProtectedMedia);
+#else   // BUILDFLAG(IS_CHROMEOS_LACROS)
+  const bool enable_cdm_factory_daemon = true;
+#endif  // else BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (enable_cdm_factory_daemon && cdm_config.use_hw_secure_codecs &&
       cdm_config.allow_distinctive_identifier) {
     auto* factory = media_interface_factory_ptr_->Get();
     if (factory) {
@@ -405,7 +410,7 @@
     }
   }
   // Fallback to use library CDM below.
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#endif  // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   // Fallback to use CdmFactory even if `use_hw_secure_codecs` is true.
@@ -555,8 +560,9 @@
   DCHECK(cdm_factory_map_.count(cdm_guid));
   cdm_factory_map_.erase(cdm_guid);
 }
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
 void MediaInterfaceProxy::OnChromeOsCdmCreated(
     const std::string& key_system,
     const media::CdmConfig& cdm_config,
@@ -584,8 +590,7 @@
   ReportCdmTypeUMA(CrosCdmType::kChromeCdm);
   factory->CreateCdm(key_system, cdm_config, std::move(callback));
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif  // defined(OS_CHROMEOS)
 
 RENDER_DOCUMENT_HOST_USER_DATA_KEY_IMPL(MediaInterfaceProxy)
 
diff --git a/content/browser/media/media_interface_proxy.h b/content/browser/media/media_interface_proxy.h
index 28bcc04..b7ce26d4 100644
--- a/content/browser/media/media_interface_proxy.h
+++ b/content/browser/media/media_interface_proxy.h
@@ -117,8 +117,9 @@
   // Callback for connection error from the CdmFactoryPtr in the
   // |cdm_factory_map_| associated with |cdm_guid|.
   void OnCdmServiceConnectionError(const base::Token& cdm_guid);
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
   // Callback for for Chrome OS CDM creation to facilitate falling back to the
   // library CDM if the daemon is unavailable or other settings prevent usage of
   // it.
@@ -129,8 +130,7 @@
       mojo::PendingRemote<media::mojom::ContentDecryptionModule> receiver,
       media::mojom::CdmContextPtr cdm_context,
       const std::string& error_message);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
   // Gets the InterfaceFactory from MediaFoundationService. May return null if
diff --git a/content/browser/media/service_factory.cc b/content/browser/media/service_factory.cc
index f2418637..95950f6 100644
--- a/content/browser/media/service_factory.cc
+++ b/content/browser/media/service_factory.cc
@@ -13,19 +13,23 @@
 #include "base/logging.h"
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/time/time.h"
-#include "content/browser/service_sandbox_type.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/service_process_host.h"
 #include "content/public/common/content_client.h"
 #include "media/base/cdm_context.h"
 #include "media/base/media_switches.h"
 #include "media/media_buildflags.h"
+#include "media/mojo/mojom/cdm_service.mojom.h"
 
 #if defined(OS_MAC)
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "sandbox/mac/seatbelt_extension.h"
 #endif  // defined(OS_MAC)
 
+#if defined(OS_WIN)
+#include "media/mojo/mojom/media_foundation_service.mojom.h"
+#endif  // defined(OS_WIN)
+
 namespace content {
 
 namespace {
diff --git a/content/browser/media/service_factory.h b/content/browser/media/service_factory.h
index 79cf5d9..426895d 100644
--- a/content/browser/media/service_factory.h
+++ b/content/browser/media/service_factory.h
@@ -8,11 +8,11 @@
 #include "base/token.h"
 #include "build/build_config.h"
 #include "content/public/common/cdm_info.h"
-#include "media/mojo/mojom/cdm_service.mojom.h"
+#include "media/mojo/mojom/cdm_service.mojom-forward.h"
 #include "url/gurl.h"
 
 #if defined(OS_WIN)
-#include "media/mojo/mojom/media_foundation_service.mojom.h"
+#include "media/mojo/mojom/media_foundation_service.mojom-forward.h"
 #endif  // defined(OS_WIN)
 
 namespace content {
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc
index 513d177..6501b99 100644
--- a/content/browser/renderer_host/clipboard_host_impl.cc
+++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -492,6 +492,79 @@
       ui::ClipboardBuffer::kCopyPaste, CreateDataEndpoint());
 }
 
+bool ClipboardHostImpl::IsUnsanitizedCustomFormatContentAllowed() {
+  if (!base::FeatureList::IsEnabled(blink::features::kClipboardCustomFormats)) {
+    mojo::ReportBadMessage("Custom format read/write is not enabled.");
+    return false;
+  }
+
+  if (render_frame_host()->HasTransientUserActivation())
+    return true;
+  return false;
+}
+
+void ClipboardHostImpl::ReadAvailableCustomAndStandardFormats(
+    ReadAvailableCustomAndStandardFormatsCallback callback) {
+  if (!IsUnsanitizedCustomFormatContentAllowed())
+    return;
+  std::vector<std::u16string> format_types =
+      ui::Clipboard::GetForCurrentThread()
+          ->ReadAvailablePlatformSpecificFormatNames(
+              ui::ClipboardBuffer::kCopyPaste, CreateDataEndpoint().get());
+  std::move(callback).Run(format_types);
+}
+
+void ClipboardHostImpl::ReadUnsanitizedCustomFormat(
+    const std::u16string& format,
+    ReadUnsanitizedCustomFormatCallback callback) {
+  if (!IsUnsanitizedCustomFormatContentAllowed())
+    return;
+  // `kMaxFormatSize` includes the null terminator as well so we check if
+  // the `format` size is strictly less than `kMaxFormatSize` or not.
+  if (format.length() >= blink::mojom::ClipboardHost::kMaxFormatSize)
+    return;
+
+  // Extract the custom format names and then query the web custom format
+  // corresponding to the MIME type.
+  std::string format_name = base::UTF16ToASCII(format);
+  auto data_endpoint = CreateDataEndpoint();
+  std::map<std::string, std::string> custom_format_names =
+      ui::Clipboard::GetForCurrentThread()->ExtractCustomPlatformNames(
+          ui::ClipboardBuffer::kCopyPaste, data_endpoint.get());
+  std::string web_custom_format_string;
+  if (custom_format_names.find(format_name) != custom_format_names.end())
+    web_custom_format_string = custom_format_names[format_name];
+  if (web_custom_format_string.empty())
+    return;
+
+  std::string result;
+  ui::Clipboard::GetForCurrentThread()->ReadData(
+      ui::ClipboardFormatType::GetType(web_custom_format_string),
+      data_endpoint.get(), &result);
+  if (result.size() >= blink::mojom::ClipboardHost::kMaxDataSize)
+    return;
+  base::span<const uint8_t> span = base::as_bytes(base::make_span(result));
+  mojo_base::BigBuffer buffer = mojo_base::BigBuffer(span);
+  std::move(callback).Run(std::move(buffer));
+}
+
+void ClipboardHostImpl::WriteUnsanitizedCustomFormat(
+    const std::u16string& format,
+    mojo_base::BigBuffer data) {
+  if (!IsUnsanitizedCustomFormatContentAllowed())
+    return;
+  // `kMaxFormatSize` & `kMaxDataSize` includes the null terminator.
+  if (format.length() >= blink::mojom::ClipboardHost::kMaxFormatSize)
+    return;
+  if (data.size() >= blink::mojom::ClipboardHost::kMaxDataSize)
+    return;
+
+  // The `format` is mapped to user agent defined web custom format before
+  // writing to the clipboard. This happens in
+  // `ScopedClipboardWriter::WriteData`.
+  clipboard_writer_->WriteData(format, std::move(data));
+}
+
 void ClipboardHostImpl::PasteIfPolicyAllowed(
     ui::ClipboardBuffer clipboard_buffer,
     const ui::ClipboardFormatType& data_type,
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h
index e920098..8778ab7 100644
--- a/content/browser/renderer_host/clipboard_host_impl.h
+++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -176,6 +176,13 @@
   void ReadCustomData(ui::ClipboardBuffer clipboard_buffer,
                       const std::u16string& type,
                       ReadCustomDataCallback callback) override;
+  void ReadAvailableCustomAndStandardFormats(
+      ReadAvailableCustomAndStandardFormatsCallback callback) override;
+  void ReadUnsanitizedCustomFormat(
+      const std::u16string& format,
+      ReadUnsanitizedCustomFormatCallback callback) override;
+  void WriteUnsanitizedCustomFormat(const std::u16string& format,
+                                    mojo_base::BigBuffer data) override;
   void WriteText(const std::u16string& text) override;
   void WriteHtml(const std::u16string& markup, const GURL& url) override;
   void WriteSvg(const std::u16string& markup) override;
@@ -190,6 +197,10 @@
   void WriteStringToFindPboard(const std::u16string& text) override;
 #endif
 
+  // Returns true if custom format is allowed to be read/written from/to the
+  // clipboard, else, fails.
+  bool IsUnsanitizedCustomFormatContentAllowed();
+
   // Called by PerformPasteIfContentAllowed() when an is allowed request is
   // needed. Virtual to be overridden in tests.
   virtual void StartIsPasteContentAllowedRequest(
diff --git a/content/browser/renderer_host/cross_process_frame_connector.cc b/content/browser/renderer_host/cross_process_frame_connector.cc
index 570752f..b035f86d 100644
--- a/content/browser/renderer_host/cross_process_frame_connector.cc
+++ b/content/browser/renderer_host/cross_process_frame_connector.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/trace_event/optional_trace_event.h"
 #include "components/viz/common/features.h"
 #include "content/browser/renderer_host/cursor_manager.h"
 #include "content/browser/renderer_host/frame_tree.h"
@@ -112,12 +113,15 @@
 }
 
 void CrossProcessFrameConnector::RenderProcessGone() {
+  OPTIONAL_TRACE_EVENT1("content",
+                        "CrossProcessFrameConnector::RenderProcessGone",
+                        "visibility", visibility_);
   has_crashed_ = true;
 
-  RenderFrameHost* rfh =
-      frame_proxy_in_parent_renderer_->frame_tree_node()->current_frame_host();
-  int process_id = rfh->GetProcess()->GetID();
-  for (rfh = rfh->GetParent(); rfh; rfh = rfh->GetParent()) {
+  RenderFrameHostImpl* current_child_rfh = current_child_frame_host();
+  int process_id = current_child_rfh->GetProcess()->GetID();
+  for (auto* rfh = current_child_rfh->GetParent(); rfh;
+       rfh = rfh->GetParent()) {
     if (rfh->GetProcess()->GetID() == process_id) {
       // The crash will be already logged by the ancestor - ignore this crash in
       // the current instance of the CrossProcessFrameConnector.
@@ -130,9 +134,34 @@
 
   frame_proxy_in_parent_renderer_->ChildProcessGone();
 
-  auto* parent_view = GetParentRenderWidgetHostView();
-  if (parent_view && parent_view->host()->delegate())
-    parent_view->host()->delegate()->SubframeCrashed(visibility_);
+  if (current_child_rfh->delegate()) {
+    // If a subframe crashed on a hidden tab, mark the tab for reload to avoid
+    // showing a sad frame to the user if they ever switch back to that tab. Do
+    // this for subframes that are either visible in viewport or visible but
+    // scrolled out of view, but skip subframes that are not rendered (e.g., via
+    // "display:none"), since in that case the user wouldn't see a sad frame
+    // anyway. Prerendering subframes do not enter this code since
+    // RenderFrameHostImpl immediately cancels prerender if a render process
+    // exits.
+    bool did_mark_for_reload = false;
+    if (current_child_rfh->delegate()->GetVisibility() != Visibility::VISIBLE &&
+        visibility_ != blink::mojom::FrameVisibility::kNotRendered &&
+        base::FeatureList::IsEnabled(
+            features::kReloadHiddenTabsWithCrashedSubframes)) {
+      frame_proxy_in_parent_renderer_->frame_tree_node()
+          ->frame_tree()
+          ->controller()
+          .SetNeedsReload(
+              NavigationControllerImpl::NeedsReloadType::kCrashedSubframe);
+      did_mark_for_reload = true;
+      UMA_HISTOGRAM_ENUMERATION(
+          "Stability.ChildFrameCrash.TabMarkedForReload.Visibility",
+          visibility_);
+    }
+
+    UMA_HISTOGRAM_BOOLEAN("Stability.ChildFrameCrash.TabMarkedForReload",
+                          did_mark_for_reload);
+  }
 }
 
 void CrossProcessFrameConnector::SendIntrinsicSizingInfoToParent(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 8e2e5085..feadbff8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3553,6 +3553,11 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
     switches::kSchedulerBoostUrgent,
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    switches::kLacrosEnablePlatformEncryptedHevc,
+    switches::kLacrosEnablePlatformHevc,
+    switches::kLacrosUseChromeosProtectedMedia,
+#endif
   };
   renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
                                  base::size(kSwitchNames));
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 308f0ab..0d5902f 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -177,12 +177,6 @@
   // warning shown to the user.
   virtual void RendererResponsive(RenderWidgetHostImpl* render_widget_host) {}
 
-  // Notification that a cross-process subframe on this page has crashed, and a
-  // sad frame is shown if the subframe was visible.  |frame_visibility|
-  // specifies whether the subframe is visible, scrolled out of view, or hidden
-  // (e.g., with "display: none").
-  virtual void SubframeCrashed(blink::mojom::FrameVisibility visibility) {}
-
   // Requests to lock the mouse. Once the request is approved or rejected,
   // GotResponseToLockMouseRequest() will be called on the requesting render
   // widget host. |privileged| means that the request is always granted, used
diff --git a/content/browser/service_sandbox_type.h b/content/browser/service_sandbox_type.h
index 4b58315d..1e37242 100644
--- a/content/browser/service_sandbox_type.h
+++ b/content/browser/service_sandbox_type.h
@@ -31,32 +31,6 @@
              : sandbox::policy::SandboxType::kNoSandbox;
 }
 
-// media::mojom::CdmServiceBroker
-namespace media {
-namespace mojom {
-class CdmServiceBroker;
-}
-}  // namespace media
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<media::mojom::CdmServiceBroker>() {
-  return sandbox::policy::SandboxType::kCdm;
-}
-
-#if defined(OS_WIN)
-// media::mojom::MediaFoundationServiceBroker
-namespace media {
-namespace mojom {
-class MediaFoundationServiceBroker;
-}
-}  // namespace media
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<media::mojom::MediaFoundationServiceBroker>() {
-  return sandbox::policy::SandboxType::kMediaFoundationCdm;
-}
-#endif  // defined(OS_WIN)
-
 // network::mojom::NetworkService
 namespace network {
 namespace mojom {
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index e0d8835..60079470 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -11712,6 +11712,7 @@
   TestNavigationObserver back_load_observer(shell()->web_contents());
   shell()->web_contents()->GetController().GoBack();
   back_load_observer.Wait();
+  new_shell->web_contents()->WasHidden();
   EXPECT_EQ(1, popup_process->VisibleClientCount());
   EXPECT_EQ(1u, popup_process->GetFrameDepth());
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index e9d924e..b743f78 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7728,32 +7728,6 @@
     delegate_->RendererResponsive(this, render_widget_host);
 }
 
-void WebContentsImpl::SubframeCrashed(
-    blink::mojom::FrameVisibility visibility) {
-  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SubframeCrashed",
-                        "visibility", visibility);
-  // If a subframe crashed on a hidden tab, mark the tab for reload to avoid
-  // showing a sad frame to the user if they ever switch back to that tab. Do
-  // this for subframes that are either visible in viewport or visible but
-  // scrolled out of view, but skip subframes that are not rendered (e.g., via
-  // "display:none"), since in that case the user wouldn't see a sad frame
-  // anyway.
-  bool did_mark_for_reload = false;
-  if (IsHidden() && visibility != blink::mojom::FrameVisibility::kNotRendered &&
-      base::FeatureList::IsEnabled(
-          features::kReloadHiddenTabsWithCrashedSubframes)) {
-    // TODO(https://crbug.com/1170273): Handle multiple controllers (MPArch)
-    GetController().SetNeedsReload(
-        NavigationControllerImpl::NeedsReloadType::kCrashedSubframe);
-    did_mark_for_reload = true;
-    UMA_HISTOGRAM_ENUMERATION(
-        "Stability.ChildFrameCrash.TabMarkedForReload.Visibility", visibility);
-  }
-
-  UMA_HISTOGRAM_BOOLEAN("Stability.ChildFrameCrash.TabMarkedForReload",
-                        did_mark_for_reload);
-}
-
 void WebContentsImpl::BeforeUnloadFiredFromRenderManager(
     bool proceed,
     const base::TimeTicks& proceed_time,
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 6921352..419619d4 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -953,7 +953,6 @@
       RenderWidgetHostImpl* render_widget_host,
       base::RepeatingClosure hang_monitor_restarter) override;
   void RendererResponsive(RenderWidgetHostImpl* render_widget_host) override;
-  void SubframeCrashed(blink::mojom::FrameVisibility visibility) override;
   void RequestToLockMouse(RenderWidgetHostImpl* render_widget_host,
                           bool user_gesture,
                           bool last_unlocked_by_target,
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index 138f587..2f82a58 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
index 59549b4..0a2da8e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 39245d19..a91bcdb 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index ba7aeee..b89cd63e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index d7c5dd6..493b5f6 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
index 918820a..4a77565 100644
--- a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
index 9a439b3e..c99df311 100644
--- a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 19ef360..3755938 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index 883b823e..492a3bda 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index ce64709..571723e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index e4fa3e19..ca14c4e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index e002dbd..cd47d234 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index d614b92..54a0421 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
@@ -755,6 +755,31 @@
 crbug.com/angleproject/3686 [ android angle-opengles ] deqp/functional/gles3/multisample/fbo_8_samples.html [ Failure ]
 crbug.com/angleproject/3686 [ android angle-opengles ] deqp/functional/gles3/multisample/fbo_max_samples.html [ Failure ]
 
+############
+# ChromeOS #
+############
+
+# Skip these, rather than expect them to fail to avoid that they impact the
+# following tests result.
+crbug.com/1237319 [ chromeos ] conformance2/reading/read-pixels-from-fbo-test.html [ Skip ]
+crbug.com/1237319 [ chromeos ] conformance2/rendering/framebuffer-render-to-layer.html [ Skip ]
+crbug.com/1237319 [ chromeos passthrough ] conformance/textures/misc/texture-size-limit.html [ Skip ]
+crbug.com/1237319 [ chromeos passthrough ] conformance2/textures/misc/tex-3d-size-limit.html [ Skip ]
+crbug.com/1237319 [ chromeos passthrough ] conformance/extensions/khr-parallel-shader-compile.html [ skip ]
+
+# Won't investigate failures on validating command decoder. Remove once it's unshipped.
+crbug.com/1237319 [ chromeos no-passthrough ] conformance/extensions/webgl-compressed-texture-astc.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance/extensions/ext-texture-compression-bptc.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance/extensions/ext-texture-compression-rgtc.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance/extensions/s3tc-and-rgtc.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance2/textures/canvas_sub_rectangle/tex-2d-r8ui-red_integer-unsigned_byte.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance2/textures/canvas_sub_rectangle/tex-3d-* [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance2/textures/misc/origin-clean-conformance-offscreencanvas.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance2/textures/webgl_canvas/tex-2d-r8ui-red_integer-unsigned_byte.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] conformance2/textures/webgl_canvas/tex-3d-* [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] deqp/functional/gles3/clipping.html [ Skip ]
+crbug.com/1237319 [ chromeos no-passthrough ] deqp/functional/gles3/negativetextureapi.html [ Skip ]
+
 crbug.com/1037958 [ mac amd-0x679e ] conformance2/transform_feedback/switching-objects.html [ RetryOnFailure ]
 
 # Conflicting expectations to test that the
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index cd3243a..9fd6d37 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
@@ -754,6 +754,8 @@
 # Skip test that breaks subsequent tests.
 crbug.com/1043953 [ chromeos chromeos-board-amd64-generic ] conformance/textures/misc/texture-size-limit.html [ Skip ]
 
+crbug.com/1237319 [ chromeos chromeos-board-eve passthrough ] conformance/textures/misc/texture-size-limit.html [ Skip ]
+crbug.com/1237319 [ chromeos angle-opengles passthrough ] conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Skip ]
 
 # Texture compression is only expected to be available on desktop GPUs.
 crbug.com/1080360 [ chromeos chromeos-board-kevin ] WebglExtension_EXT_texture_compression_bptc [ Skip ]
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index 80ac68d..674cc3b 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -24,7 +24,7 @@
 # Devices
 # tags: [ android-nexus-5 android-nexus-5x android-nexus-6 android-nexus-9
 #             android-pixel-2 android-pixel-4 android-shield-android-tv
-#         chromeos-board-amd64-generic chromeos-board-kevin
+#         chromeos-board-amd64-generic chromeos-board-kevin chromeos-board-eve
 #         fuchsia-board-astro fuchsia-board-qemu-x64 ]
 # Platform
 # tags: [ desktop
diff --git a/content/test/mock_clipboard_host.cc b/content/test/mock_clipboard_host.cc
index 346e9d269..a54129a 100644
--- a/content/test/mock_clipboard_host.cc
+++ b/content/test/mock_clipboard_host.cc
@@ -176,6 +176,36 @@
   needs_reset_ = true;
 }
 
+void MockClipboardHost::ReadAvailableCustomAndStandardFormats(
+    ReadAvailableCustomAndStandardFormatsCallback callback) {
+  std::vector<std::u16string> format_names;
+  for (const auto& item : unsanitized_custom_data_map_)
+    format_names.emplace_back(item.first);
+  std::move(callback).Run(format_names);
+}
+
+void MockClipboardHost::ReadUnsanitizedCustomFormat(
+    const std::u16string& format,
+    ReadUnsanitizedCustomFormatCallback callback) {
+  const auto it = unsanitized_custom_data_map_.find(format);
+  if (it == unsanitized_custom_data_map_.end())
+    return;
+
+  mojo_base::BigBuffer buffer = mojo_base::BigBuffer(
+      base::make_span(it->second.data(), it->second.size()));
+  std::move(callback).Run(std::move(buffer));
+}
+
+void MockClipboardHost::WriteUnsanitizedCustomFormat(
+    const std::u16string& format,
+    mojo_base::BigBuffer data) {
+  if (needs_reset_)
+    Reset();
+  // Simulate the underlying platform copying this data.
+  std::vector<uint8_t> data_copy(data.data(), data.data() + data.size());
+  unsanitized_custom_data_map_[format] = data_copy;
+}
+
 #if defined(OS_MAC)
 void MockClipboardHost::WriteStringToFindPboard(const std::u16string& text) {}
 #endif
diff --git a/content/test/mock_clipboard_host.h b/content/test/mock_clipboard_host.h
index 368695a2..9e9fe38a 100644
--- a/content/test/mock_clipboard_host.h
+++ b/content/test/mock_clipboard_host.h
@@ -60,6 +60,13 @@
                      const std::u16string& title) override;
   void WriteImage(const SkBitmap& bitmap) override;
   void CommitWrite() override;
+  void ReadAvailableCustomAndStandardFormats(
+      ReadAvailableCustomAndStandardFormatsCallback callback) override;
+  void ReadUnsanitizedCustomFormat(
+      const std::u16string& format,
+      ReadUnsanitizedCustomFormatCallback callback) override;
+  void WriteUnsanitizedCustomFormat(const std::u16string& format,
+                                    mojo_base::BigBuffer data) override;
 #if defined(OS_MAC)
   void WriteStringToFindPboard(const std::u16string& text) override;
 #endif
@@ -74,6 +81,7 @@
   std::map<std::u16string, std::u16string> custom_data_;
   bool write_smart_paste_ = false;
   bool needs_reset_ = false;
+  std::map<std::u16string, std::vector<uint8_t>> unsanitized_custom_data_map_;
 
   DISALLOW_COPY_AND_ASSIGN(MockClipboardHost);
 };
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
index b7c41f9..bacfae8 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -782,6 +782,12 @@
                                       properties->eir.value());
   }
 
+  // There is no guarantee that BatteryAdded is called after DeviceAdded
+  // (for the same device). So always update the battery value of this newly
+  // detected device in case we ignored BatteryAdded calls for it before this
+  // DeviceAdded call.
+  UpdateDeviceBatteryLevelFromBatteryClient(object_path);
+
   for (auto& observer : observers_)
     observer.DeviceAdded(this, device_bluez);
 }
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.cc b/fuchsia/engine/browser/web_engine_content_browser_client.cc
index 05339b0..28dcf9e 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.cc
@@ -37,6 +37,7 @@
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
+#include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -175,6 +176,7 @@
     int child_process_id) {
   // TODO(https://crbug.com/1083520): Pass based on process type.
   constexpr char const* kSwitchesToCopy[] = {
+      blink::switches::kSharedArrayBufferAllowedOrigins,
       switches::kCorsExemptHeaders,
       switches::kEnableCastStreamingReceiver,
       switches::kEnableContentDirectories,
diff --git a/fuchsia/engine/web_instance_host/web_instance_host.cc b/fuchsia/engine/web_instance_host/web_instance_host.cc
index 8624deaa..7f32d8bf 100644
--- a/fuchsia/engine/web_instance_host/web_instance_host.cc
+++ b/fuchsia/engine/web_instance_host/web_instance_host.cc
@@ -350,6 +350,7 @@
     return true;
 
   static const base::StringPiece kAllowedArgs[] = {
+      blink::switches::kSharedArrayBufferAllowedOrigins,
       blink::switches::kGpuRasterizationMSAASampleCount,
       blink::switches::kMinHeightForGpuRasterTile,
       cc::switches::kEnableClippedImageScaling,
diff --git a/gpu/command_buffer/service/shared_image_video.cc b/gpu/command_buffer/service/shared_image_video.cc
index 5c65b707..a4569cc 100644
--- a/gpu/command_buffer/service/shared_image_video.cc
+++ b/gpu/command_buffer/service/shared_image_video.cc
@@ -34,9 +34,66 @@
 #include "third_party/skia/include/core/SkPromiseImageTexture.h"
 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "ui/gl/gl_utils.h"
 
 namespace gpu {
 
+namespace {
+class VideoImage : public gl::GLImage {
+ public:
+  VideoImage(AHardwareBuffer* buffer, base::ScopedFD begin_read_fence)
+      : handle_(base::android::ScopedHardwareBufferHandle::Create(buffer)),
+        begin_read_fence_(std::move(begin_read_fence)) {}
+
+  // gl::GLImage:
+  std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
+  GetAHardwareBuffer() override {
+    return std::make_unique<ScopedHardwareBufferFenceSyncImpl>(
+        this, base::android::ScopedHardwareBufferHandle::Create(handle_.get()),
+        std::move(begin_read_fence_));
+  }
+
+  base::ScopedFD TakeEndReadFence() { return std::move(end_read_fence_); }
+
+ protected:
+  ~VideoImage() override = default;
+
+ private:
+  class ScopedHardwareBufferFenceSyncImpl
+      : public base::android::ScopedHardwareBufferFenceSync {
+   public:
+    ScopedHardwareBufferFenceSyncImpl(
+        scoped_refptr<VideoImage> image,
+        base::android::ScopedHardwareBufferHandle handle,
+        base::ScopedFD fence_fd)
+        : ScopedHardwareBufferFenceSync(std::move(handle),
+                                        std::move(fence_fd),
+                                        base::ScopedFD(),
+                                        /*is_video=*/true),
+          image_(std::move(image)) {}
+    ~ScopedHardwareBufferFenceSyncImpl() override = default;
+
+    void SetReadFence(base::ScopedFD fence_fd, bool has_context) override {
+      image_->end_read_fence_ =
+          gl::MergeFDs(std::move(image_->end_read_fence_), std::move(fence_fd));
+    }
+
+   private:
+    scoped_refptr<VideoImage> image_;
+  };
+
+  base::android::ScopedHardwareBufferHandle handle_;
+
+  // This fence should be waited upon before reading from the buffer.
+  base::ScopedFD begin_read_fence_;
+
+  // This fence should be waited upon to ensure that the reader is finished
+  // reading from the buffer.
+  base::ScopedFD end_read_fence_;
+};
+
+}  // namespace
+
 SharedImageVideo::SharedImageVideo(
     const Mailbox& mailbox,
     const gfx::Size& size,
@@ -205,7 +262,7 @@
   }
 
   bool BeginAccess(GLenum mode) override {
-    scoped_lock_ = GetScopedDrDcLock();
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
 
     // This representation should only be called for read or overlay.
     DCHECK(mode == GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM ||
@@ -216,11 +273,10 @@
     return true;
   }
 
-  void EndAccess() override { scoped_lock_.reset(); }
+  void EndAccess() override {}
 
  private:
   std::unique_ptr<gles2::AbstractTexture> texture_;
-  std::unique_ptr<base::AutoLockMaybe> scoped_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTextureVideo);
 };
@@ -253,7 +309,7 @@
   }
 
   bool BeginAccess(GLenum mode) override {
-    scoped_lock_ = GetScopedDrDcLock();
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
 
     // This representation should only be called for read or overlay.
     DCHECK(mode == GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM ||
@@ -264,12 +320,11 @@
     return true;
   }
 
-  void EndAccess() override { scoped_lock_.reset(); }
+  void EndAccess() override {}
 
  private:
   std::unique_ptr<gles2::AbstractTexture> abstract_texture_;
   scoped_refptr<gles2::TexturePassthrough> passthrough_texture_;
-  std::unique_ptr<base::AutoLockMaybe> scoped_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTexturePassthroughVideo);
 };
@@ -307,7 +362,7 @@
       std::vector<GrBackendSemaphore>* begin_semaphores,
       std::vector<GrBackendSemaphore>* end_semaphores,
       std::unique_ptr<GrBackendSurfaceMutableState>* end_state) override {
-    scoped_lock_ = GetScopedDrDcLock();
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
 
     DCHECK(!scoped_hardware_buffer_);
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
@@ -361,6 +416,7 @@
   }
 
   void EndReadAccess() override {
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
     DCHECK(scoped_hardware_buffer_);
 
     SharedImageRepresentationSkiaVkAndroid::EndReadAccess();
@@ -371,13 +427,11 @@
     scoped_hardware_buffer_->SetReadFence(android_backing()->TakeReadFence(),
                                           true);
     scoped_hardware_buffer_ = nullptr;
-    scoped_lock_.reset();
   }
 
  private:
   std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
       scoped_hardware_buffer_;
-  std::unique_ptr<base::AutoLockMaybe> scoped_lock_;
 };
 
 // TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
@@ -518,8 +572,7 @@
 
  protected:
   bool BeginReadAccess(std::vector<gfx::GpuFence>* acquire_fences) override {
-    scoped_lock_ = GetScopedDrDcLock();
-
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
     // A |CodecImage| is already in a SurfaceView, render content to the
     // overlay.
     if (!stream_image()->HasTextureOwner()) {
@@ -531,16 +584,35 @@
   }
 
   void EndReadAccess(gfx::GpuFenceHandle release_fence) override {
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
     DCHECK(release_fence.is_null());
-    scoped_lock_.reset();
+    if (gl_image_) {
+      DCHECK(scoped_hardware_buffer_);
+      scoped_hardware_buffer_->SetReadFence(gl_image_->TakeEndReadFence(),
+                                            true);
+      gl_image_.reset();
+      scoped_hardware_buffer_.reset();
+    }
   }
 
   gl::GLImage* GetGLImage() override {
-    AssertAcquiredDrDcLock();
-
+    base::AutoLockMaybe auto_lock(GetDrDcLockPtr());
     DCHECK(stream_image()->HasTextureOwner())
         << "The backing is already in a SurfaceView!";
-    return stream_image();
+
+    // Note that we have SurfaceView overlay as well as SurfaceControl.
+    // SurfaceView may/may not have TextureOwner whereas SurfaceControl always
+    // have TextureOwner. It is not possible to know whether we are in
+    // SurfaceView or SurfaceControl mode in Begin/EndReadAccess. Hence
+    // |scoped_hardware_buffer_| and |gl_image_| needs to be created here since
+    // GetGLImage will only be called for SurfaceControl.
+    if (!gl_image_) {
+      scoped_hardware_buffer_ = stream_image()->GetAHardwareBuffer();
+      gl_image_ = base::MakeRefCounted<VideoImage>(
+          scoped_hardware_buffer_->buffer(),
+          scoped_hardware_buffer_->TakeFence());
+    }
+    return gl_image_.get();
   }
 
   void NotifyOverlayPromotion(bool promotion,
@@ -550,7 +622,9 @@
   }
 
  private:
-  std::unique_ptr<base::AutoLockMaybe> scoped_lock_;
+  std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
+      scoped_hardware_buffer_;
+  scoped_refptr<VideoImage> gl_image_;
 
   StreamTextureSharedImageInterface* stream_image() {
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 97214f7f..789fcb9f 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -270,7 +270,7 @@
     LOGGER.info(line)
     sys.stdout.flush()
   LOGGER.debug('Finished print_process_output.')
-  return out
+  return out.decode('utf-8') if sys.version_info.major == 3 else out
 
 
 def get_current_xcode_info():
diff --git a/ios/build/bots/scripts/xcode_log_parser.py b/ios/build/bots/scripts/xcode_log_parser.py
index 2180338..1a062df3 100644
--- a/ios/build/bots/scripts/xcode_log_parser.py
+++ b/ios/build/bots/scripts/xcode_log_parser.py
@@ -11,6 +11,7 @@
 import re
 import shutil
 import subprocess
+import sys
 
 import file_util
 import test_runner
@@ -27,6 +28,22 @@
 _XCRESULT_SUFFIX = '.xcresult'
 
 
+def _sanitize_str(line):
+  """Encodes str when in python 2."""
+  if sys.version_info.major == 2:
+    if isinstance(line, unicode):
+      line = line.encode('utf-8')
+  return line
+
+
+def _sanitize_str_list(lines):
+  """Encodes any unicode in list when in python 2."""
+  sanitized_lines = []
+  for line in lines:
+    sanitized_lines.append(_sanitize_str(line))
+  return sanitized_lines
+
+
 def get_parser():
   """Returns correct parser from version of Xcode installed."""
   if xcode_util.using_xcode_11_or_higher():
@@ -55,11 +72,9 @@
   def _find_list_of_tests(tests, regex):
     """Adds test names matched by regex to result list."""
     for test_line in output:
-      m_test = regex.search(test_line.decode("utf-8"))
+      m_test = regex.search(test_line)
       if m_test:
-        tests.append(
-            '%s/%s' %
-            (m_test.group(1).encode('utf-8'), m_test.group(2).encode('utf-8')))
+        tests.append('%s/%s' % (m_test.group(1), m_test.group(2)))
 
   _find_list_of_tests(passed_tests, passed_test_regex)
   _find_list_of_tests(failed_tests, failed_test_regex)
@@ -82,10 +97,11 @@
   Returns:
     (str) Test case id in format TestClass/TestMethod.
   """
+  test_case = _sanitize_str(test_case)
   test = test_case.replace('[', '').replace(']',
                                             '').replace('-',
                                                         '').replace(' ', '/')
-  return test.encode('utf8') if isinstance(test, unicode) else test
+  return test
 
 
 def copy_screenshots_for_failed_test(failure_message, test_case_folder):
@@ -178,11 +194,11 @@
       return failed
     for failure_summary in actions_invocation_record['issues'][
         'testFailureSummaries']['_values']:
-      error_line = failure_summary['documentLocationInCreatingWorkspace'][
-          'url']['_value'].encode('utf8')
-      fail_message = [
-          error_line
-      ] + failure_summary['message']['_value'].encode('utf8').splitlines()
+      error_line = _sanitize_str(
+          failure_summary['documentLocationInCreatingWorkspace']['url']
+          ['_value'])
+      fail_message = [error_line] + _sanitize_str(
+          failure_summary['message']['_value']).splitlines()
       test_case_id = format_test_case(failure_summary['testCaseName']['_value'])
       failed[test_case_id] = fail_message
     return failed
@@ -209,7 +225,7 @@
           # can be parsed from root.
           continue
         for test in test_suite['subtests']['_values']:
-          test_name = test['identifier']['_value'].encode('utf8')
+          test_name = _sanitize_str(test['identifier']['_value'])
           if any(
               test_name.endswith(suffix)
               for suffix in SYSTEM_ERROR_TEST_NAME_SUFFIXES):
@@ -224,14 +240,13 @@
                     xcresult, test['summaryRef']['id']['_value']))
             failure_message = []
             for failure in rootFailure['failureSummaries']['_values']:
-              file_name = failure.get('fileName', {}).get('_value',
-                                                          '').encode('utf8')
-              line_number = failure.get('lineNumber', {}).get('_value',
-                                                              '').encode('utf8')
+              file_name = _sanitize_str(
+                  failure.get('fileName', {}).get('_value', ''))
+              line_number = _sanitize_str(
+                  failure.get('lineNumber', {}).get('_value', ''))
               failure_location = 'file: %s, line: %s' % (file_name, line_number)
-              failure_message += [
-                  failure_location
-              ] + failure['message']['_value'].encode('utf8').splitlines()
+              failure_message += [failure_location] + _sanitize_str(
+                  failure['message']['_value']).splitlines()
             results['failed'][test_name] = failure_message
 
   @staticmethod
@@ -252,6 +267,8 @@
           }
         }
     """
+    output_path = _sanitize_str(output_path)
+    output = _sanitize_str_list(output)
     LOGGER.info('Reading %s' % output_path)
     test_results = {
         'passed': [],
@@ -283,8 +300,8 @@
       test_results['failed']['BUILD_INTERRUPTED'] = [
           '%s with test results does not exist.' % xcresult
       ] + output
-      passed_tests, failed_tests_dict = parse_passed_failed_tests_for_interrupted_run(
-          output)
+      passed_tests, failed_tests_dict = (
+          parse_passed_failed_tests_for_interrupted_run(output))
       test_results['passed'] = passed_tests
       test_results['failed'].update(failed_tests_dict)
       return test_results
@@ -545,14 +562,16 @@
           }
       }
     """
+    output_folder = _sanitize_str(output_folder)
+    output = _sanitize_str_list(output)
     test_results = {'passed': [], 'failed': {}}
     plist_path = os.path.join(output_folder, 'Info.plist')
     if not os.path.exists(plist_path):
       test_results['failed']['BUILD_INTERRUPTED'] = [
           '%s with test results does not exist.' % plist_path
       ] + output
-      passed_tests, failed_tests_dict = parse_passed_failed_tests_for_interrupted_run(
-          output)
+      passed_tests, failed_tests_dict = (
+          parse_passed_failed_tests_for_interrupted_run(output))
       test_results['passed'] = passed_tests
       test_results['failed'].update(failed_tests_dict)
       return test_results
@@ -567,7 +586,7 @@
         if ('ErrorSummaries' in action_result and
             action_result['ErrorSummaries']):
           test_results['failed']['TESTS_DID_NOT_START'].append('\n'.join(
-              error_summary['Message']
+              _sanitize_str(error_summary['Message'])
               for error_summary in action_result['ErrorSummaries']))
       else:
         summary_plist = os.path.join(
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index b768ecf6..fec99a27 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -471,17 +471,7 @@
       for attempt, attempt_results in enumerate(shard_attempts):
 
         for test in attempt_results['failed'].keys():
-          # TODO(crbug.com/1178923): Remove unicode check when it's figured out
-          # where unicode is introduced.
-          log_lines = []
-          for line in self.logs.get(test, []):
-            if sys.version_info.major == 2:
-              if isinstance(line, unicode):
-                LOGGER.warning('Unicode string: %s' % line)
-                line = line.encode('utf-8')
-            log_lines.append(line)
-
-          output.mark_failed(test, test_log='\n'.join(log_lines))
+          output.mark_failed(test, test_log='\n'.join(self.logs.get(test, [])))
 
         # 'aborted tests' in logs is an array of strings, each string defined
         # as "{TestCase}/{testMethod}"
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index e694d24..0819b778b 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -502,10 +502,6 @@
     {"url-blocklist-ios", flag_descriptions::kURLBlocklistIOSName,
      flag_descriptions::kURLBlocklistIOSDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kURLBlocklistIOS)},
-    {"enable-ios-managed-settings-ui",
-     flag_descriptions::kEnableIOSManagedSettingsUIName,
-     flag_descriptions::kEnableIOSManagedSettingsUIDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kEnableIOSManagedSettingsUI)},
     {"new-content-suggestions-feed", flag_descriptions::kDiscoverFeedInNtpName,
      flag_descriptions::kDiscoverFeedInNtpDescription, flags_ui::kOsIos,
      FEATURE_WITH_PARAMS_VALUE_TYPE(kDiscoverFeedInNtp,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index c1564c9..3510998d 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -212,11 +212,6 @@
     "Enables the option of capturing an entire webpage as a PDF when a "
     "screenshot is taken.";
 
-const char kEnableIOSManagedSettingsUIName[] = "Enable IOS Managed Settings UI";
-const char kEnableIOSManagedSettingsUIDescription[] =
-    "Enable showing a different UI when the setting is managed by an "
-    "enterprise policy on iOS.";
-
 const char kEnableManualPasswordGenerationName[] =
     "Enable manual password generation.";
 const char kEnableManualPasswordGenerationDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 269cb48..2dade21 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -186,11 +186,6 @@
 extern const char kEnableFullPageScreenshotName[];
 extern const char kEnableFullPageScreenshotDescription[];
 
-// Title and description for the flag to enable to show a different UI when the
-// setting is managed by an enterprise policy.
-extern const char kEnableIOSManagedSettingsUIName[];
-extern const char kEnableIOSManagedSettingsUIDescription[];
-
 // Title and description for the flag to enable UI that allows the user to
 // create a strong password even if the field wasn't parsed as a new password
 // field.
diff --git a/ios/chrome/browser/infobars/infobar_ios.h b/ios/chrome/browser/infobars/infobar_ios.h
index da90fee..07f522be 100644
--- a/ios/chrome/browser/infobars/infobar_ios.h
+++ b/ios/chrome/browser/infobars/infobar_ios.h
@@ -62,6 +62,11 @@
   bool high_priority() const { return high_priority_; }
   void set_high_priority(bool high_priority);
 
+  // Whether or not this infobar reference has been removed from its owning
+  // InfobarManager.
+  bool removed_from_owner() { return removed_from_owner_; }
+  void set_removed_from_owner() { removed_from_owner_ = true; }
+
   // Returns a weak pointer to the infobar.
   base::WeakPtr<InfoBarIOS> GetWeakPtr();
 
@@ -77,6 +82,7 @@
   bool accepted_ = false;
   bool skip_banner_ = false;
   bool high_priority_ = false;
+  bool removed_from_owner_ = false;
   base::WeakPtrFactory<InfoBarIOS> weak_factory_{this};
 };
 
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_overlay_request_callback_installer.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_overlay_request_callback_installer.mm
index a3474702..3c25bf3 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_overlay_request_callback_installer.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_overlay_request_callback_installer.mm
@@ -72,7 +72,7 @@
   InfoBarControllerDelegate* infobar_controller_delegate =
       static_cast<InfoBarControllerDelegate*>(infobar);
   if (!infobar || !infobar_controller_delegate->IsOwned() ||
-      !infobar->delegate())
+      !infobar->delegate() || infobar->removed_from_owner())
     return;
 
   infobar_controller_delegate->RemoveInfoBar();
diff --git a/ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.mm b/ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.mm
index 28b0a41b..81f75ed 100644
--- a/ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.h"
 
 #include "base/check.h"
+#include "base/feature_list.h"
 #include "components/infobars/core/infobar.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
@@ -18,6 +19,9 @@
 using infobars::InfoBar;
 using infobars::InfoBarManager;
 
+const base::Feature kInfobarRemoveCheck{"InfobarRemoveCheck",
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
+
 #pragma mark - InfobarOverlayRequestCancelHandler
 
 InfobarOverlayRequestCancelHandler::InfobarOverlayRequestCancelHandler(
@@ -62,6 +66,9 @@
     infobars::InfoBar* infobar,
     bool animate) {
   if (cancel_handler_->infobar() == infobar) {
+    if (base::FeatureList::IsEnabled(kInfobarRemoveCheck)) {
+      static_cast<InfoBarIOS*>(infobar)->set_removed_from_owner();
+    }
     cancel_handler_->CancelForInfobarRemoval();
     // The cancel handler is destroyed after Cancel(), so no code can be added
     // after this call.
@@ -72,6 +79,9 @@
     InfoBar* old_infobar,
     InfoBar* new_infobar) {
   if (cancel_handler_->infobar() == old_infobar) {
+    if (base::FeatureList::IsEnabled(kInfobarRemoveCheck)) {
+      static_cast<InfoBarIOS*>(old_infobar)->set_removed_from_owner();
+    }
     cancel_handler_->HandleReplacement(static_cast<InfoBarIOS*>(new_infobar));
     cancel_handler_->CancelForInfobarRemoval();
     // The cancel handler is destroyed after Cancel(), so no code can be added
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
index dad4cbd3..d11a9b0 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
@@ -150,9 +150,7 @@
   // This pref is also used for logging. If it is changed, change it in the
   // other places.
   std::vector<std::string> prefs_vector = {prefs::kArticlesForYouEnabled};
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI)) {
-    prefs_vector.push_back(prefs::kNTPContentSuggestionsEnabled);
-  }
+  prefs_vector.push_back(prefs::kNTPContentSuggestionsEnabled);
 
   auto provider = std::make_unique<RemoteSuggestionsProviderImpl>(
       service, prefs, GetApplicationContext()->GetApplicationLocale(),
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
index cbccacf26..6134ad7 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
@@ -105,7 +105,7 @@
 }
 
 - (void)stop {
-  [self.viewController dismissViewControllerAnimated:YES completion:nil];
+  [self.baseViewController dismissViewControllerAnimated:YES completion:nil];
   self.viewController = nil;
 
   self.mediator = nil;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 057719d5..d3437ad5 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -181,8 +181,7 @@
 
   self.contentSuggestionsEnabled =
       prefs->GetBoolean(prefs::kArticlesForYouEnabled) &&
-      (!base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) ||
-       prefs->GetBoolean(prefs::kNTPContentSuggestionsEnabled));
+      prefs->GetBoolean(prefs::kNTPContentSuggestionsEnabled);
   self.contentSuggestionsExpanded = [[PrefBackedBoolean alloc]
       initWithPrefService:prefs
                  prefName:feed::prefs::kArticlesListVisible];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index dc4378c..61f5833e 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -917,8 +917,7 @@
 
 - (BOOL)contentSuggestionsEnabled {
   return self.articleForYouEnabled->GetValue()->GetBool() &&
-         (!base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) ||
-          self.contentSuggestionsPolicyEnabled->GetValue()->GetBool());
+         self.contentSuggestionsPolicyEnabled->GetValue()->GetBool();
 }
 
 #pragma mark - PrefObserverDelegate
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index 29655c7b..0b5bca2 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -930,8 +930,7 @@
 
   NSArray* collectionActions = [self collectionItems];
 
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-      _browserPolicyConnector &&
+  if (_browserPolicyConnector &&
       _browserPolicyConnector->HasMachineLevelPolicies()) {
     // Show enterprise infomation when chrome is managed by policy and the
     // settings UI flag is enabled.
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
index 4b8ac55..a08af75 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
@@ -469,10 +469,6 @@
 // Tests that the "Managed by..." item is hidden when none of the policies is
 // set.
 TEST_F(PopupMenuMediatorTest, TestEnterpriseInfoHidden) {
-  // Enabled the feature flag.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(kEnableIOSManagedSettingsUI);
-
   CreateMediator(PopupMenuTypeToolsMenu, /*is_incognito=*/NO,
                  /*trigger_incognito_hint=*/NO);
 
@@ -486,10 +482,6 @@
 
 // Tests that the "Managed by..." item is shown.
 TEST_F(PopupMenuMediatorTest, TestEnterpriseInfoShown) {
-  // Enabled the feature flag.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(kEnableIOSManagedSettingsUI);
-
   // Set a policy.
   base::ScopedTempDir state_directory;
   ASSERT_TRUE(state_directory.CreateUniqueTempDir());
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
index c936084..52ef186 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_table_view_controller.mm
@@ -165,8 +165,7 @@
   TableViewModel* model = self.tableViewModel;
 
   [model addSectionWithIdentifier:SectionIdentifierSwitches];
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-      _browser->GetBrowserState()->GetPrefs()->IsManagedPreference(
+  if (_browser->GetBrowserState()->GetPrefs()->IsManagedPreference(
           autofill::prefs::kAutofillCreditCardEnabled)) {
     [model addItem:[self cardManagedItem]
         toSectionWithIdentifier:SectionIdentifierSwitches];
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
index f149c334..0910cf1 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_table_view_controller.mm
@@ -120,8 +120,7 @@
 
   [model addSectionWithIdentifier:SectionIdentifierSwitches];
 
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-      _browserState->GetPrefs()->IsManagedPreference(
+  if (_browserState->GetPrefs()->IsManagedPreference(
           autofill::prefs::kAutofillProfileEnabled)) {
     [model addItem:[self managedAddressItem]
         toSectionWithIdentifier:SectionIdentifierSwitches];
diff --git a/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm b/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
index 396e6be3..8f88e1ea 100644
--- a/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
@@ -118,8 +118,7 @@
   // Block popups switch.
   [model addSectionWithIdentifier:SectionIdentifierMainSwitch];
 
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-      _browserState->GetPrefs()->IsManagedPreference(
+  if (_browserState->GetPrefs()->IsManagedPreference(
           prefs::kManagedDefaultPopupsSetting)) {
     _blockPopupsManagedItem = [self blockPopupsManagedItem];
     [model addItem:_blockPopupsManagedItem
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
index 7f634e0..c740712 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
@@ -715,8 +715,7 @@
           kAllowSigninItemAccessibilityIdentifier;
       [items addObject:allowSigninItem];
     }
-    if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-        self.userPrefService->IsManagedPreference(
+    if (self.userPrefService->IsManagedPreference(
             prefs::kSearchSuggestEnabled)) {
       TableViewInfoButtonItem* autocompleteItem = [self
           TableViewInfoButtonItemType:AutocompleteSearchesAndURLsManagedItemType
@@ -736,8 +735,7 @@
                         dataType:0];
       [items addObject:autocompleteItem];
     }
-    if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-        self.userPrefService->IsManagedPreference(
+    if (self.userPrefService->IsManagedPreference(
             prefs::kSafeBrowsingEnabled)) {
       TableViewInfoButtonItem* safeBrowsingManagedItem = [self
           TableViewInfoButtonItemType:AutocompleteSearchesAndURLsManagedItemType
@@ -760,8 +758,7 @@
       [items addObject:safeBrowsingItem];
     }
     [items addObject:self.passwordLeakCheckItem];
-    if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-        self.localPrefService->IsManagedPreference(
+    if (self.localPrefService->IsManagedPreference(
             metrics::prefs::kMetricsReportingEnabled)) {
       TableViewInfoButtonItem* improveChromeItem = [self
           TableViewInfoButtonItemType:ImproveChromeManagedItemType
@@ -781,8 +778,7 @@
                         dataType:0];
       [items addObject:improveChromeItem];
     }
-    if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-        self.userPrefService->IsManagedPreference(
+    if (self.userPrefService->IsManagedPreference(
             unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled)) {
       TableViewInfoButtonItem* betterSearchAndBrowsingItem = [self
           TableViewInfoButtonItemType:BetterSearchAndBrowsingManagedItemType
diff --git a/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
index 51478c2..ff5ab14 100644
--- a/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/language/language_settings_table_view_controller.mm
@@ -130,8 +130,7 @@
   [self populateLanguagesSection];
 
   [model addSectionWithIdentifier:SectionIdentifierTranslate];
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-      self.dataSource.translateManaged) {
+  if (self.dataSource.translateManaged) {
     // Translate managed item.
     TableViewInfoButtonItem* translateManagedItem =
         [[TableViewInfoButtonItem alloc] initWithType:ItemTypeTranslateManaged];
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index 7f7b2ab5..8d9b870 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -399,8 +399,7 @@
   if (!self.navigationItem.searchController.active) {
     [model addSectionWithIdentifier:SectionIdentifierSavePasswordsSwitch];
 
-    if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-        _browserState->GetPrefs()->IsManagedPreference(
+    if (_browserState->GetPrefs()->IsManagedPreference(
             password_manager::prefs::kCredentialsEnableService)) {
       // TODO(crbug.com/1082827): observe the managing status of the pref.
       // Show managed settings UI when the pref is managed by the policy.
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 1ce54138..7e0fcd1 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -444,8 +444,7 @@
   }
 
   // Show managed UI if default search engine is managed by policy.
-  if (base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) &&
-      [self isDefaultSearchEngineManagedByPolicy]) {
+  if ([self isDefaultSearchEngineManagedByPolicy]) {
     if (@available(iOS 14, *)) {
       [model addItem:[self managedSearchEngineItem]
           toSectionWithIdentifier:SettingsSectionIdentifierDefaults];
@@ -480,8 +479,7 @@
       toSectionWithIdentifier:SettingsSectionIdentifierAdvanced];
   [model addItem:[self privacyDetailItem]
       toSectionWithIdentifier:SettingsSectionIdentifierAdvanced];
-  if (!base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI) ||
-      [_contentSuggestionPolicyEnabled value]) {
+  if ([_contentSuggestionPolicyEnabled value]) {
     [model addItem:self.articlesForYouItem
         toSectionWithIdentifier:SettingsSectionIdentifierAdvanced];
 
@@ -1798,9 +1796,6 @@
     self.articlesForYouItem.on = [_articlesEnabled value];
     [self reconfigureCellsForItems:@[ self.articlesForYouItem ]];
   } else if (observableBoolean == _contentSuggestionPolicyEnabled) {
-    if (!base::FeatureList::IsEnabled(kEnableIOSManagedSettingsUI))
-      return;
-
     NSIndexPath* itemIndexPath;
     NSInteger itemTypeToRemove;
     TableViewItem* itemToAdd;
diff --git a/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm b/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm
index 84721ef..a57ef39 100644
--- a/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm
@@ -192,8 +192,6 @@
 
   [coordinator start];
 
-  [vc_partial_mock verify];
-
   // Verify that the positioning is correct.
   auto activityHandler =
       static_cast<id<ActivityServicePositioner, ActivityServicePresentation>>(
@@ -203,13 +201,11 @@
       CGRectEqualToRect(fake_origin_view_.bounds, activityHandler.sourceRect));
 
   // Verify that the presentation protocol works too.
-  id activity_vc_partial_mock = OCMPartialMock(activityViewController);
-  [[activity_vc_partial_mock expect] dismissViewControllerAnimated:YES
-                                                        completion:nil];
+  [[vc_partial_mock expect] dismissViewControllerAnimated:YES completion:nil];
 
   [activityHandler activityServiceDidEndPresenting];
 
-  [activity_vc_partial_mock verify];
+  [vc_partial_mock verify];
 }
 
 // Tests that the coordinator handles the QRGenerationCommands protocol.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm
index d6858d4e..091bea2 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/features.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -294,11 +295,10 @@
   if (IsTabsBulkActionsEnabled()) {
     UIImageView* selectIconView = [[UIImageView alloc] init];
     selectIconView.translatesAutoresizingMaskIntoConstraints = NO;
-    selectIconView.contentMode = UIViewContentModeCenter;
+    selectIconView.contentMode = UIViewContentModeScaleAspectFit;
     selectIconView.hidden = !self.isInSelectionMode;
 
-    selectIconView.image = [[self selectIconImageForCurrentState]
-        imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    selectIconView.image = [self selectIconImageForCurrentState];
 
     [topBar addSubview:selectIconView];
     _selectIconView = selectIconView;
@@ -348,6 +348,10 @@
 
   if (_selectIconView) {
     _selectIconConstraints = @[
+      [_selectIconView.heightAnchor
+          constraintEqualToConstant:kGridCellSelectIconSize],
+      [_selectIconView.widthAnchor
+          constraintEqualToConstant:kGridCellSelectIconSize],
       [titleLabel.trailingAnchor
           constraintEqualToAnchor:_selectIconView.leadingAnchor
                          constant:-kGridCellTitleLabelContentInset],
@@ -355,7 +359,7 @@
           constraintEqualToAnchor:_selectIconView.centerYAnchor],
       [_selectIconView.trailingAnchor
           constraintEqualToAnchor:topBar.trailingAnchor
-                         constant:-kGridCellCloseButtonContentInset],
+                         constant:-kGridCellSelectIconContentInset],
 
     ];
   }
@@ -392,7 +396,9 @@
 - (UIImage*)selectIconImageForCurrentState {
   if (@available(iOS 13, *)) {
     if (_state == GridCellStateEditingUnselected) {
-      return [UIImage systemImageNamed:@"circle"];
+      return [[UIImage systemImageNamed:@"circle"]
+          imageWithTintColor:[UIColor cr_systemGray3Color]
+               renderingMode:UIImageRenderingModeAlwaysOriginal];
     }
     return [UIImage systemImageNamed:@"checkmark.circle.fill"];
   }
@@ -445,8 +451,7 @@
   _state = state;
 
   _closeTapTargetButton.enabled = !self.isInSelectionMode;
-  self.selectIconView.image = [[self selectIconImageForCurrentState]
-      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+  self.selectIconView.image = [self selectIconImageForCurrentState];
 
   [self configureCloseOrSelectIconConstraints];
   self.border.hidden = self.isInSelectionMode;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h
index 6d11cf9..7c915da 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h
@@ -89,6 +89,8 @@
 extern const CGFloat kGridCellCloseButtonContentInset;
 extern const CGFloat kGridCellTitleLabelContentInset;
 extern const CGFloat kGridCellIconDiameter;
+extern const CGFloat kGridCellSelectIconContentInset;
+extern const CGFloat kGridCellSelectIconSize;
 extern const CGFloat kGridCellSelectionRingGapWidth;
 extern const CGFloat kGridCellSelectionRingTintWidth;
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.mm
index b3fe76d..aa047b4 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.mm
@@ -83,6 +83,8 @@
 const CGFloat kGridCellCloseButtonContentInset = 8.5f;
 const CGFloat kGridCellTitleLabelContentInset = 4.0f;
 const CGFloat kGridCellIconDiameter = 16.0f;
+const CGFloat kGridCellSelectIconContentInset = 4.0f;
+const CGFloat kGridCellSelectIconSize = 25.0f;
 const CGFloat kGridCellSelectionRingGapWidth = 2.0f;
 const CGFloat kGridCellSelectionRingTintWidth = 5.0f;
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm
index a5c2c4f6..d5d9b76 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/ui/menu/menu_histograms.h"
 #import "ios/chrome/browser/ui/menu/tab_context_menu_delegate.h"
 #import "ios/chrome/browser/ui/ntp/ntp_util.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/features.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_menu_actions_data_source.h"
@@ -140,11 +141,14 @@
           }
         }
 
-        if ([weakSelf.contextMenuDelegate
-                respondsToSelector:@selector(selectTabs)]) {
-          [menuElements addObject:[actionFactory actionToSelectTabsWithBlock:^{
-                          [weakSelf.contextMenuDelegate selectTabs];
-                        }]];
+        if (IsTabsBulkActionsEnabled()) {
+          if ([weakSelf.contextMenuDelegate
+                  respondsToSelector:@selector(selectTabs)]) {
+            [menuElements
+                addObject:[actionFactory actionToSelectTabsWithBlock:^{
+                  [weakSelf.contextMenuDelegate selectTabs];
+                }]];
+          }
         }
         if ([weakSelf.contextMenuDelegate
                 respondsToSelector:@selector(closeTabWithIdentifier:
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
index c9ac354..5a04202 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
@@ -240,7 +240,18 @@
 
 - (void)setMode:(TabGridMode)mode {
   _mode = mode;
-  [self.collectionView reloadData];
+
+  NSRange allSectionsRange =
+      NSMakeRange(/*location=*/0, self.collectionView.numberOfSections);
+  NSIndexSet* allSectionsIndexSet =
+      [NSIndexSet indexSetWithIndexesInRange:allSectionsRange];
+  // Reloading specific sections in a |performBatchUpdates| fades the changes in
+  // rather than reloads the collection view with a harsh flash.
+  [self.collectionView
+      performBatchUpdates:^{
+        [self.collectionView reloadSections:allSectionsIndexSet];
+      }
+               completion:nil];
 
   // Clear items when exiting selection mode.
   if (mode == TabGridModeNormal) {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
index 207c137..ef5ea528 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -1194,7 +1194,14 @@
 }
 
 // Tests sharing multiple tabs from the tab grid edit mode.
-- (void)testTabGridBulkActionShare {
+// TODO(crbug.com/1238501): The pasteboard is "not available at this time" when
+// running on device.
+#if TARGET_OS_SIMULATOR
+#define MAYBE_testTabGridBulkActionShare testTabGridBulkActionShare
+#else
+#define MAYBE_testTabGridBulkActionShare DISABLED_testTabGridBulkActionShare
+#endif
+- (void)MAYBE_testTabGridBulkActionShare {
   if (!base::ios::IsRunningOnIOS14OrLater()) {
     EARL_GREY_TEST_SKIPPED(
         @"Bulk actions are only supported on iOS 14 and later.");
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 60999de..186b2cb 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -19,9 +19,6 @@
 const base::Feature kTestFeature{"TestFeature",
                                  base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kEnableIOSManagedSettingsUI{
-    "EnableIOSManagedSettingsUI", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kSharedHighlightingIOS{"SharedHighlightingIOS",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index 4ce8455..e2e75a07 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -25,10 +25,6 @@
 // features in tests works.
 extern const base::Feature kTestFeature;
 
-// Feature flag to enable showing a different UI when the setting is managed by
-// an enterprise policy.
-extern const base::Feature kEnableIOSManagedSettingsUI;
-
 // Feature flag to enable Shared Highlighting (Link to Text).
 extern const base::Feature kSharedHighlightingIOS;
 
diff --git a/ios/chrome/common/credential_provider/BUILD.gn b/ios/chrome/common/credential_provider/BUILD.gn
index e22c8c72..28db84b 100644
--- a/ios/chrome/common/credential_provider/BUILD.gn
+++ b/ios/chrome/common/credential_provider/BUILD.gn
@@ -12,6 +12,8 @@
     "archivable_credential.mm",
     "archivable_credential_store.h",
     "archivable_credential_store.mm",
+    "archivable_credential_util.h",
+    "archivable_credential_util.mm",
     "as_password_credential_identity+credential.h",
     "as_password_credential_identity+credential.mm",
     "constants.h",
diff --git a/ios/chrome/common/credential_provider/archivable_credential_store.mm b/ios/chrome/common/credential_provider/archivable_credential_store.mm
index 4a51e85..eac645a 100644
--- a/ios/chrome/common/credential_provider/archivable_credential_store.mm
+++ b/ios/chrome/common/credential_provider/archivable_credential_store.mm
@@ -98,8 +98,9 @@
                                        options:0
                                          error:&error];
   DCHECK(!error) << base::SysNSStringToUTF8(error.description);
-  NSSet* classes = [NSSet setWithObjects:[ArchivableCredential class],
-                                         [NSMutableDictionary class], nil];
+  NSSet* classes =
+      [NSSet setWithObjects:[ArchivableCredential class],
+                            [NSMutableDictionary class], [NSString class], nil];
   NSMutableDictionary<NSString*, ArchivableCredential*>* dictionary =
       [NSKeyedUnarchiver unarchivedObjectOfClasses:classes
                                           fromData:data
diff --git a/ios/chrome/common/credential_provider/archivable_credential_util.h b/ios/chrome/common/credential_provider/archivable_credential_util.h
new file mode 100644
index 0000000..bb4e26d7
--- /dev/null
+++ b/ios/chrome/common/credential_provider/archivable_credential_util.h
@@ -0,0 +1,15 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_ARCHIVABLE_CREDENTIAL_UTIL_H_
+#define IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_ARCHIVABLE_CREDENTIAL_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+// Constructs a record identifier for the given data. This should be as close
+// as possible to |RecordIdentifierForPasswordForm|, as this is what is used
+// to detect if a credential should be updated instead of created.
+NSString* RecordIdentifierForData(NSURL* url, NSString* username);
+
+#endif  // IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_ARCHIVABLE_CREDENTIAL_UTIL_H_
diff --git a/ios/chrome/common/credential_provider/archivable_credential_util.mm b/ios/chrome/common/credential_provider/archivable_credential_util.mm
new file mode 100644
index 0000000..ef556b7
--- /dev/null
+++ b/ios/chrome/common/credential_provider/archivable_credential_util.mm
@@ -0,0 +1,31 @@
+// 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 "ios/chrome/common/credential_provider/archivable_credential_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* RecordIdentifierForData(NSURL* url, NSString* username) {
+  NSURLComponents* urlComponents = [NSURLComponents componentsWithURL:url
+                                              resolvingAgainstBaseURL:NO];
+
+  // Remove specific parts of URL that are thrown away in the credential
+  // manager.
+  urlComponents.user = nil;
+  urlComponents.password = nil;
+  urlComponents.query = nil;
+  urlComponents.fragment = nil;
+
+  NSString* strippedURL = urlComponents.string;
+
+  // Replace path with / as well to end up with origin.
+  urlComponents.path = @"/";
+
+  NSString* origin = urlComponents.string;
+
+  return
+      [NSString stringWithFormat:@"%@||%@||%@", strippedURL, username, origin];
+}
diff --git a/ios/chrome/common/credential_provider/user_defaults_credential_store.mm b/ios/chrome/common/credential_provider/user_defaults_credential_store.mm
index 1eddca9..d1c0996 100644
--- a/ios/chrome/common/credential_provider/user_defaults_credential_store.mm
+++ b/ios/chrome/common/credential_provider/user_defaults_credential_store.mm
@@ -69,8 +69,9 @@
     return [[NSMutableDictionary alloc] init];
   }
   NSError* error = nil;
-  NSSet* classes = [NSSet setWithObjects:[ArchivableCredential class],
-                                         [NSMutableDictionary class], nil];
+  NSSet* classes =
+      [NSSet setWithObjects:[ArchivableCredential class],
+                            [NSMutableDictionary class], [NSString class], nil];
   NSMutableDictionary<NSString*, ArchivableCredential*>* dictionary =
       [NSKeyedUnarchiver unarchivedObjectOfClasses:classes
                                           fromData:data
diff --git a/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist b/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist
index 34d673a..42fe8d8 100644
--- a/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist
+++ b/ios/chrome/credential_provider_extension/credential_provider_extension_localize_strings_config.plist
@@ -29,6 +29,7 @@
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_EMPTY_CREDENTIALS_TITLE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_ENTER</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_CANCEL</string>
+				<string>IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_REPLACE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_HELP_ACCESSIBILITY_LABEL</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_ERROR_MESSAGE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_ERROR_TITLE</string>
@@ -38,6 +39,8 @@
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_TITLE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_PASSWORD</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_PASSWORD_PLACEHOLDER</string>
+				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_TITLE</string>
+				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_MESSAGE</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_USERNAME</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_USERNAME_PLACEHOLDER</string>
 				<string>IDS_IOS_CREDENTIAL_PROVIDER_NO_SEARCH_RESULTS</string>
diff --git a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
index 23839a81..34294ea 100644
--- a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
+++ b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings.grd
@@ -209,6 +209,9 @@
       <message name="IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_CANCEL" desc="Title for cancel buttons" meaning="Title for buttons meant to cancel an action [CHAR_LIMIT=10]">
         Cancel
       </message>
+      <message name="IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_REPLACE" desc="Title for replace buttons" meaning="Title for buttons where the action replaces a password [CHAR_LIMIT=10]">
+        Replace
+      </message>
       <message name="IDS_IOS_CREDENTIAL_PROVIDER_HELP_ACCESSIBILITY_LABEL" desc="Used as the accessibility label read by screen readers for a Help button." meaning="[iOS only][CHAR_LIMIT=30]">
         Help
       </message>
@@ -236,6 +239,12 @@
       <message name="IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_PASSWORD_PLACEHOLDER" desc="Placeholder for password field">
         password
       </message>
+      <message name="IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_TITLE" desc="Title for alert asking user if they want to replace a password">
+        Replace Password?
+      </message>
+      <message name="IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_MESSAGE" desc="Message for alert asking user if they want to replace a password">
+        You already saved a password for "<ph name="USERNAME">$1<ex>johndoe</ex></ph>" at <ph name="WEBSITE">$2<ex>google.com</ex></ph>. Do you want to replace it?
+      </message>
       <message name="IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_USERNAME" desc="Title for row to type in username">
         Username
       </message>
diff --git a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_REPLACE.png.sha1 b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_REPLACE.png.sha1
new file mode 100644
index 0000000..9c31c0d
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_REPLACE.png.sha1
@@ -0,0 +1 @@
+8ee3dfae4e11ced386e1dd04a2e4c1b362d0b53b
\ No newline at end of file
diff --git a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_MESSAGE.png.sha1 b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_MESSAGE.png.sha1
new file mode 100644
index 0000000..9c31c0d
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_MESSAGE.png.sha1
@@ -0,0 +1 @@
+8ee3dfae4e11ced386e1dd04a2e4c1b362d0b53b
\ No newline at end of file
diff --git a/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_TITLE.png.sha1 b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_TITLE.png.sha1
new file mode 100644
index 0000000..9c31c0d
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/strings/ios_credential_provider_extension_strings_grd/IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_TITLE.png.sha1
@@ -0,0 +1 @@
+8ee3dfae4e11ced386e1dd04a2e4c1b362d0b53b
\ No newline at end of file
diff --git a/ios/chrome/credential_provider_extension/ui/BUILD.gn b/ios/chrome/credential_provider_extension/ui/BUILD.gn
index 25cebe9..fdd95d6 100644
--- a/ios/chrome/credential_provider_extension/ui/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/ui/BUILD.gn
@@ -27,6 +27,7 @@
     "new_password_mediator.mm",
     "new_password_table_cell.h",
     "new_password_table_cell.mm",
+    "new_password_ui_handler.h",
     "new_password_view_controller.h",
     "new_password_view_controller.mm",
     "stale_credentials_view_controller.h",
@@ -86,3 +87,18 @@
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "new_password_mediator_unittest.mm" ]
+  deps = [
+    ":ui",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/common/app_group",
+    "//ios/chrome/common/credential_provider",
+    "//ios/chrome/credential_provider_extension:password_util",
+    "//testing/gtest",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
index 49b3b73b..e6885f2 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_coordinator.mm
@@ -174,7 +174,8 @@
   self.createPasswordCoordinator = [[NewPasswordCoordinator alloc]
       initWithBaseViewController:self.viewController
                          context:self.context
-              serviceIdentifiers:self.serviceIdentifiers];
+              serviceIdentifiers:self.serviceIdentifiers
+             existingCredentials:self.credentialStore];
   [self.createPasswordCoordinator start];
 }
 
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_coordinator.h b/ios/chrome/credential_provider_extension/ui/new_password_coordinator.h
index 02346d67..00bf2d8 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_coordinator.h
+++ b/ios/chrome/credential_provider_extension/ui/new_password_coordinator.h
@@ -9,6 +9,7 @@
 
 @class ASCredentialProviderExtensionContext;
 @class ASCredentialServiceIdentifier;
+@protocol CredentialStore;
 
 // The coordinator for the new password feature.
 @interface NewPasswordCoordinator : NSObject
@@ -20,6 +21,7 @@
                        context:(ASCredentialProviderExtensionContext*)context
             serviceIdentifiers:
                 (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers
+           existingCredentials:(id<CredentialStore>)existingCredentials
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_coordinator.mm b/ios/chrome/credential_provider_extension/ui/new_password_coordinator.mm
index e434cf79..7941c9a 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_coordinator.mm
+++ b/ios/chrome/credential_provider_extension/ui/new_password_coordinator.mm
@@ -6,6 +6,7 @@
 
 #import <AuthenticationServices/AuthenticationServices.h>
 
+#include "ios/chrome/common/app_group/app_group_constants.h"
 #import "ios/chrome/common/credential_provider/credential.h"
 #import "ios/chrome/credential_provider_extension/password_util.h"
 #import "ios/chrome/credential_provider_extension/ui/new_password_mediator.h"
@@ -34,6 +35,10 @@
 @property(nonatomic, strong)
     NSArray<ASCredentialServiceIdentifier*>* serviceIdentifiers;
 
+// The existing credentials to check for whether a new credential already
+// exists.
+@property(nonatomic, weak) id<CredentialStore> existingCredentials;
+
 @end
 
 @implementation NewPasswordCoordinator
@@ -42,19 +47,24 @@
     initWithBaseViewController:(UIViewController*)baseViewController
                        context:(ASCredentialProviderExtensionContext*)context
             serviceIdentifiers:
-                (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers {
+                (NSArray<ASCredentialServiceIdentifier*>*)serviceIdentifiers
+           existingCredentials:(id<CredentialStore>)existingCredentials {
   self = [super init];
   if (self) {
     _baseViewController = baseViewController;
     _context = context;
     _serviceIdentifiers = serviceIdentifiers;
+    _existingCredentials = existingCredentials;
   }
   return self;
 }
 
 - (void)start {
   self.mediator = [[NewPasswordMediator alloc]
-      initWithServiceIdentifier:self.serviceIdentifiers.firstObject];
+      initWithUserDefaults:app_group::GetGroupUserDefaults()
+         serviceIdentifier:self.serviceIdentifiers.firstObject];
+  self.mediator.existingCredentials = self.existingCredentials;
+  self.mediator.context = self.context;
 
   NewPasswordViewController* newPasswordViewController =
       [[NewPasswordViewController alloc] init];
@@ -63,6 +73,12 @@
   newPasswordViewController.navigationItem.prompt =
       PromptForServiceIdentifiers(self.serviceIdentifiers);
 
+  self.mediator.uiHandler = newPasswordViewController;
+
+  NSString* identifier = self.serviceIdentifiers.firstObject.identifier;
+  NSURL* url = identifier ? [NSURL URLWithString:identifier] : nil;
+  newPasswordViewController.currentHost = url.host;
+
   self.viewController = [[UINavigationController alloc]
       initWithRootViewController:newPasswordViewController];
   [self.baseViewController presentViewController:self.viewController
@@ -84,14 +100,4 @@
   [self.baseViewController dismissViewControllerAnimated:YES completion:nil];
 }
 
-- (void)userSelectedCredential:(id<Credential>)credential {
-  NSString* password =
-      PasswordWithKeychainIdentifier(credential.keychainIdentifier);
-  ASPasswordCredential* ASCredential =
-      [ASPasswordCredential credentialWithUser:credential.user
-                                      password:password];
-  [self.context completeRequestWithSelectedCredential:ASCredential
-                                    completionHandler:nil];
-}
-
 @end
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_mediator.h b/ios/chrome/credential_provider_extension/ui/new_password_mediator.h
index 2fefa747..7be7186 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_mediator.h
+++ b/ios/chrome/credential_provider_extension/ui/new_password_mediator.h
@@ -10,15 +10,34 @@
 #import "ios/chrome/credential_provider_extension/ui/new_password_view_controller.h"
 
 @class ASCredentialServiceIdentifier;
+@class ASCredentialProviderExtensionContext;
+@protocol CredentialStore;
+@protocol NewPasswordUIHandler;
 
 // This mediator fetches requirements and saves new credentials for its
 // consumer.
 @interface NewPasswordMediator : NSObject <NewCredentialHandler>
 
-- (instancetype)initWithServiceIdentifier:
-    (ASCredentialServiceIdentifier*)serviceIdentifier NS_DESIGNATED_INITIALIZER;
+// Initializes a new object, using |userDefaults| as the user defaults location
+// to store new credentials to and |serviceIdentifier| as the current service to
+// store new credentials for.
+- (instancetype)initWithUserDefaults:(NSUserDefaults*)userDefaults
+                   serviceIdentifier:
+                       (ASCredentialServiceIdentifier*)serviceIdentifier
+    NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
+
+// Existing credential store to check to see if a new credential already
+// exists.
+@property(nonatomic, weak) id<CredentialStore> existingCredentials;
+
+// UI handler to allow this mediator to ask the UI for any necessary updates.
+@property(nonatomic, weak) id<NewPasswordUIHandler> uiHandler;
+
+// The extension context for the credential provider.
+@property(nonatomic, weak) ASCredentialProviderExtensionContext* context;
+
 @end
 
 #endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_NEW_PASSWORD_MEDIATOR_H_
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_mediator.mm b/ios/chrome/credential_provider_extension/ui/new_password_mediator.mm
index 4bab35859..5746d3ca 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_mediator.mm
+++ b/ios/chrome/credential_provider_extension/ui/new_password_mediator.mm
@@ -7,10 +7,15 @@
 #import <AuthenticationServices/AuthenticationServices.h>
 
 #include "ios/chrome/common/app_group/app_group_constants.h"
+#include "ios/chrome/common/app_group/app_group_metrics.h"
 #import "ios/chrome/common/credential_provider/archivable_credential.h"
+#import "ios/chrome/common/credential_provider/archivable_credential_util.h"
 #import "ios/chrome/common/credential_provider/constants.h"
+#import "ios/chrome/common/credential_provider/credential_store.h"
 #import "ios/chrome/common/credential_provider/user_defaults_credential_store.h"
+#import "ios/chrome/credential_provider_extension/metrics_util.h"
 #import "ios/chrome/credential_provider_extension/password_util.h"
+#import "ios/chrome/credential_provider_extension/ui/new_password_ui_handler.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -21,14 +26,19 @@
 // The service identifier the password is being created for,
 @property(nonatomic, strong) ASCredentialServiceIdentifier* serviceIdentifier;
 
+// The NSUserDefaults new credentials should be stored to.
+@property(nonatomic, strong) NSUserDefaults* userDefaults;
+
 @end
 
 @implementation NewPasswordMediator
 
-- (instancetype)initWithServiceIdentifier:
-    (ASCredentialServiceIdentifier*)serviceIdentifier {
+- (instancetype)initWithUserDefaults:(NSUserDefaults*)userDefaults
+                   serviceIdentifier:
+                       (ASCredentialServiceIdentifier*)serviceIdentifier {
   self = [super init];
   if (self) {
+    _userDefaults = userDefaults;
     _serviceIdentifier = serviceIdentifier;
   }
   return self;
@@ -36,17 +46,56 @@
 
 #pragma mark - NewCredentialHandler
 
+- (void)saveCredentialWithUsername:(NSString*)username
+                          password:(NSString*)password
+                     shouldReplace:(BOOL)shouldReplace {
+  if (!shouldReplace && [self credentialExistsForUsername:username]) {
+    [self.uiHandler alertUserCredentialExists];
+    return;
+  }
+
+  ArchivableCredential* credential =
+      [self createNewCredentialWithUsername:username password:password];
+
+  if (!credential) {
+    [self.uiHandler alertSavePasswordFailed];
+    return;
+  }
+
+  [self
+      saveNewCredential:credential
+             completion:^(NSError* error) {
+               if (error) {
+                 UpdateUMACountForKey(
+                     app_group::kCredentialExtensionSaveCredentialFailureCount);
+                 [self.uiHandler alertSavePasswordFailed];
+                 return;
+               }
+               [self userSelectedCredential:credential];
+             }];
+}
+
+#pragma mark - Private
+
+// Checks whether a credential already exists with the given username.
+- (BOOL)credentialExistsForUsername:(NSString*)username {
+  NSURL* url = [NSURL URLWithString:self.serviceIdentifier.identifier];
+  NSString* recordIdentifier = RecordIdentifierForData(url, username);
+
+  return [self.existingCredentials
+      credentialWithRecordIdentifier:recordIdentifier];
+}
+
+// Creates a new credential but doesn't add it to any stores.
 - (ArchivableCredential*)createNewCredentialWithUsername:(NSString*)username
                                                 password:(NSString*)password {
+  NSURL* url = [NSURL URLWithString:self.serviceIdentifier.identifier];
+  NSString* recordIdentifier = RecordIdentifierForData(url, username);
+
   NSString* uuid = [[NSUUID UUID] UUIDString];
   if (!StorePasswordInKeychain(password, uuid)) {
     return nil;
   }
-
-  NSURL* url = [NSURL URLWithString:self.serviceIdentifier.identifier];
-  NSString* recordIdentifier =
-      [NSString stringWithFormat:@"CPE|%@|%@|%@", url.host, username,
-                                 self.serviceIdentifier.identifier];
   NSString* validationIdentifier =
       AppGroupUserDefaultsCredentialProviderUserID();
 
@@ -61,15 +110,33 @@
       validationIdentifier:validationIdentifier];
 }
 
+// Saves the given credential to disk and calls |completion| once the operation
+// is finished.
 - (void)saveNewCredential:(ArchivableCredential*)credential
                completion:(void (^)(NSError* error))completion {
   NSString* key = AppGroupUserDefaultsCredentialProviderNewCredentials();
   UserDefaultsCredentialStore* store = [[UserDefaultsCredentialStore alloc]
-      initWithUserDefaults:app_group::GetGroupUserDefaults()
+      initWithUserDefaults:self.userDefaults
                        key:key];
 
-  [store addCredential:credential];
+  if ([store credentialWithRecordIdentifier:credential.recordIdentifier]) {
+    [store updateCredential:credential];
+  } else {
+    [store addCredential:credential];
+  }
+
   [store saveDataWithCompletion:completion];
 }
 
+// Alerts the host app that the user selected a credential.
+- (void)userSelectedCredential:(id<Credential>)credential {
+  NSString* password =
+      PasswordWithKeychainIdentifier(credential.keychainIdentifier);
+  ASPasswordCredential* ASCredential =
+      [ASPasswordCredential credentialWithUser:credential.user
+                                      password:password];
+  [self.context completeRequestWithSelectedCredential:ASCredential
+                                    completionHandler:nil];
+}
+
 @end
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm b/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm
new file mode 100644
index 0000000..ad82596
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm
@@ -0,0 +1,255 @@
+// 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 "ios/chrome/credential_provider_extension/ui/new_password_mediator.h"
+
+#import <AuthenticationServices/AuthenticationServices.h>
+#import <Foundation/Foundation.h>
+
+#import "base/test/ios/wait_util.h"
+#include "ios/chrome/common/app_group/app_group_constants.h"
+#import "ios/chrome/common/credential_provider/archivable_credential.h"
+#import "ios/chrome/common/credential_provider/archivable_credential_store.h"
+#import "ios/chrome/common/credential_provider/constants.h"
+#import "ios/chrome/common/credential_provider/user_defaults_credential_store.h"
+#import "ios/chrome/credential_provider_extension/password_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Fake implementation of NewPasswordUIHandler so tests can tell if any UI
+// methods were called
+@interface FakeNewPasswordUIHandler : NSObject <NewPasswordUIHandler>
+
+// Whether the |-alertUserCredentialExists| method was called.
+@property(nonatomic, assign) BOOL alertedCredentialExists;
+// Whether the |-alertSavePasswordFailed| method was called.
+@property(nonatomic, assign) BOOL alertedSaveFailed;
+
+@end
+
+@implementation FakeNewPasswordUIHandler
+
+- (void)alertUserCredentialExists {
+  self.alertedCredentialExists = YES;
+}
+
+- (void)alertSavePasswordFailed {
+  self.alertedSaveFailed = YES;
+}
+
+@end
+
+// Fake implementation of ASCredentialProviderExtensionContext so tests can
+// tell when a credential has been saved.
+@interface FakeExtensionContext : ASCredentialProviderExtensionContext
+
+@property(nonatomic, strong) ASPasswordCredential* credential;
+
+@property(nonatomic, strong) void (^receivedCredentialBlock)();
+
+@end
+
+@implementation FakeExtensionContext
+
+- (void)completeRequestWithSelectedCredential:(ASPasswordCredential*)credential
+                            completionHandler:
+                                (void (^)(BOOL expired))completionHandler {
+  self.credential = credential;
+  if (completionHandler) {
+    completionHandler(NO);
+  }
+  if (self.receivedCredentialBlock) {
+    self.receivedCredentialBlock();
+  }
+}
+
+@end
+
+namespace {
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForFileOperationTimeout;
+
+NSString* const testWebsiteBase = @"https://wwww.example.com";
+NSString* const testWebsite =
+    [NSString stringWithFormat:@"%@/test?page=1", testWebsiteBase];
+
+NSUserDefaults* TestUserDefaults() {
+  return [NSUserDefaults standardUserDefaults];
+}
+
+ArchivableCredential* TestCredential(NSString* recordIdentifier) {
+  return [[ArchivableCredential alloc] initWithFavicon:@"favicon"
+                                    keychainIdentifier:@"keychainIdentifier"
+                                                  rank:5
+                                      recordIdentifier:recordIdentifier
+                                     serviceIdentifier:@"serviceIdentifier"
+                                           serviceName:@"serviceName"
+                                                  user:@"user"
+                                  validationIdentifier:@"validationIdentifier"];
+}
+
+class NewPasswordMediatorTest : public PlatformTest {
+ public:
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  ASCredentialServiceIdentifier* serviceIdentifier_ =
+      [[ASCredentialServiceIdentifier alloc]
+          initWithIdentifier:testWebsite
+                        type:ASCredentialServiceIdentifierTypeURL];
+  NewPasswordMediator* mediator_ =
+      [[NewPasswordMediator alloc] initWithUserDefaults:TestUserDefaults()
+                                      serviceIdentifier:serviceIdentifier_];
+  id<MutableCredentialStore> store_;
+  FakeNewPasswordUIHandler* uiHandler_ =
+      [[FakeNewPasswordUIHandler alloc] init];
+  FakeExtensionContext* context_ = [[FakeExtensionContext alloc] init];
+};
+
+void NewPasswordMediatorTest::SetUp() {
+  PlatformTest::SetUp();
+  NSString* key = AppGroupUserDefaultsCredentialProviderNewCredentials();
+  [TestUserDefaults() removeObjectForKey:key];
+
+  store_ = [[UserDefaultsCredentialStore alloc]
+      initWithUserDefaults:TestUserDefaults()
+                       key:key];
+
+  mediator_.existingCredentials = store_;
+  mediator_.uiHandler = uiHandler_;
+  mediator_.context = context_;
+}
+
+void NewPasswordMediatorTest::TearDown() {
+  PlatformTest::TearDown();
+  NSString* key = AppGroupUserDefaultsCredentialProviderNewCredentials();
+  [TestUserDefaults() removeObjectForKey:key];
+}
+
+// Tests that |-saveNewCredential:completion:| adds a new credential to the
+// store and that gets saved to disk.
+TEST_F(NewPasswordMediatorTest, SaveNewCredential) {
+  // Manually store a credential.
+  ArchivableCredential* tempCredential = TestCredential(@"abc");
+  [store_ addCredential:tempCredential];
+  EXPECT_EQ(1u, store_.credentials.count);
+  __block BOOL blockWaitCompleted = NO;
+  [store_ saveDataWithCompletion:^(NSError* error) {
+    EXPECT_FALSE(error);
+    blockWaitCompleted = YES;
+  }];
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^BOOL {
+    return blockWaitCompleted;
+  }));
+
+  // Create a second credential with a new record identifier and make sure it
+  // gets saved to disk.
+  NSString* testUsername = @"user";
+  NSString* testPassword = @"password";
+
+  context_.receivedCredentialBlock = ^() {
+    blockWaitCompleted = YES;
+  };
+
+  blockWaitCompleted = NO;
+  [mediator_ saveCredentialWithUsername:testUsername
+                               password:testPassword
+                          shouldReplace:NO];
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^BOOL {
+    return blockWaitCompleted;
+  }));
+
+  EXPECT_FALSE(uiHandler_.alertedCredentialExists);
+  EXPECT_FALSE(uiHandler_.alertedSaveFailed);
+
+  EXPECT_NSEQ(testUsername, context_.credential.user);
+  EXPECT_NSEQ(testPassword, context_.credential.password);
+
+  // Reload the store from memory and check that the credential was added.
+  NSString* key = AppGroupUserDefaultsCredentialProviderNewCredentials();
+  UserDefaultsCredentialStore* freshCredentialStore =
+      [[UserDefaultsCredentialStore alloc]
+          initWithUserDefaults:TestUserDefaults()
+                           key:key];
+  EXPECT_TRUE(freshCredentialStore);
+  EXPECT_TRUE(freshCredentialStore.credentials);
+  EXPECT_EQ(2u, freshCredentialStore.credentials.count);
+  EXPECT_NSEQ(testUsername, freshCredentialStore.credentials[1].user);
+}
+
+// Tests that |-saveNewCredential:completion:| updates an existing credential
+// and that gets saved to disk.
+TEST_F(NewPasswordMediatorTest, SaveUpdateCredential) {
+  // Create a credential that will be stored.
+  NSString* recordIdentifier = [NSString
+      stringWithFormat:@"%@/test||user||%@/", testWebsiteBase, testWebsiteBase];
+
+  // Create an initial credential with a known record identifier and store that
+  // one to disk.
+  ArchivableCredential* tempCredential = TestCredential(recordIdentifier);
+  [store_ addCredential:tempCredential];
+  EXPECT_EQ(1u, store_.credentials.count);
+  __block BOOL blockWaitCompleted = NO;
+  [store_ saveDataWithCompletion:^(NSError* error) {
+    EXPECT_FALSE(error);
+    blockWaitCompleted = YES;
+  }];
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^BOOL {
+    return blockWaitCompleted;
+  }));
+
+  // Store the originally created credential and that should update the existing
+  // one.
+  context_.receivedCredentialBlock = ^() {
+    blockWaitCompleted = YES;
+  };
+
+  // The first attempt to save should fail because the user hasn't be notified
+  // that their credentials are being replaced.
+  blockWaitCompleted = NO;
+  NSString* testUsername = @"user";
+  NSString* testPassword = @"password";
+  [mediator_ saveCredentialWithUsername:testUsername
+                               password:testPassword
+                          shouldReplace:NO];
+
+  EXPECT_TRUE(uiHandler_.alertedCredentialExists);
+  EXPECT_FALSE(uiHandler_.alertedSaveFailed);
+  EXPECT_FALSE(blockWaitCompleted);
+  uiHandler_.alertedCredentialExists = NO;
+
+  // The second attempt to save should succeed.
+  blockWaitCompleted = NO;
+  [mediator_ saveCredentialWithUsername:testUsername
+                               password:testPassword
+                          shouldReplace:YES];
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^BOOL {
+    return blockWaitCompleted;
+  }));
+
+  EXPECT_FALSE(uiHandler_.alertedCredentialExists);
+  EXPECT_FALSE(uiHandler_.alertedSaveFailed);
+
+  EXPECT_NSEQ(testUsername, context_.credential.user);
+  EXPECT_NSEQ(testPassword, context_.credential.password);
+
+  // Reload the store from memory and check that the credential was updated.
+  NSString* key = AppGroupUserDefaultsCredentialProviderNewCredentials();
+  UserDefaultsCredentialStore* freshCredentialStore =
+      [[UserDefaultsCredentialStore alloc]
+          initWithUserDefaults:TestUserDefaults()
+                           key:key];
+  EXPECT_TRUE(freshCredentialStore);
+  EXPECT_TRUE(freshCredentialStore.credentials);
+  EXPECT_EQ(1u, freshCredentialStore.credentials.count);
+  EXPECT_NSEQ(testUsername, freshCredentialStore.credentials.firstObject.user);
+}
+}
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_ui_handler.h b/ios/chrome/credential_provider_extension/ui/new_password_ui_handler.h
new file mode 100644
index 0000000..982fb26
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/new_password_ui_handler.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_NEW_PASSWORD_UI_HANDLER_H_
+#define IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_NEW_PASSWORD_UI_HANDLER_H_
+
+// Protocol to allow the NewPasswordMediator to interact with the UI
+@protocol NewPasswordUIHandler
+
+// Asks the UI to alert the user that the credential they are trying to create
+// already exists.
+- (void)alertUserCredentialExists;
+
+// Asks the UI to alert the user that the saving process failed.
+- (void)alertSavePasswordFailed;
+
+@end
+
+#endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_NEW_PASSWORD_UI_HANDLER_H_
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h
index e56f544..0d056ce 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h
+++ b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h
@@ -7,6 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/credential_provider_extension/ui/new_password_ui_handler.h"
+
 @class ArchivableCredential;
 @protocol Credential;
 @class NewPasswordViewController;
@@ -17,32 +19,32 @@
 - (void)navigationCancelButtonWasPressedInNewPasswordViewController:
     (NewPasswordViewController*)viewController;
 
-// Called when the user selects a given credential
-- (void)userSelectedCredential:(id<Credential>)credential;
-
 @end
 
 @protocol NewCredentialHandler
 
-// Called when the user wants to create a new credential.
-- (ArchivableCredential*)createNewCredentialWithUsername:(NSString*)username
-                                                password:(NSString*)password;
-
-// Saves the given credential to disk and calls |completion| once the operation
-// is finished.
-- (void)saveNewCredential:(ArchivableCredential*)credential
-               completion:(void (^)(NSError* error))completion;
+// Asks the handler to save a credential with the given |username| and
+// |password|. If |shouldReplace| is true, then the user has already been warned
+// that they may be replacing an existing credential. Otherwise, the handler
+// should not replace an existing credential.
+- (void)saveCredentialWithUsername:(NSString*)username
+                          password:(NSString*)password
+                     shouldReplace:(BOOL)shouldReplace;
 
 @end
 
 // View Controller where a user can create a new credential and use a suggested
 // password.
-@interface NewPasswordViewController : UITableViewController
+@interface NewPasswordViewController
+    : UITableViewController <NewPasswordUIHandler>
 
 @property(nonatomic, weak) id<NewPasswordViewControllerDelegate> delegate;
 
 @property(nonatomic, weak) id<NewCredentialHandler> credentialHandler;
 
+// The host for the password being generated.
+@property(nonatomic, strong) NSString* currentHost;
+
 @end
 
 #endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_NEW_PASSWORD_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm
index 38f24eec..ee34f97 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm
@@ -9,7 +9,6 @@
 #import "ios/chrome/common/credential_provider/archivable_credential.h"
 #import "ios/chrome/common/credential_provider/constants.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
-#import "ios/chrome/credential_provider_extension/metrics_util.h"
 #import "ios/chrome/credential_provider_extension/ui/new_password_table_cell.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -171,45 +170,44 @@
 
 // Action for save button.
 - (void)saveButtonWasPressed {
+  [self saveCredential:NO];
+}
+
+- (NSString*)currentUsername {
   NSIndexPath* usernameIndexPath =
       [NSIndexPath indexPathForRow:NewPasswordTableCellTypeUsername
                          inSection:0];
   NewPasswordTableCell* usernameCell =
       [self.tableView cellForRowAtIndexPath:usernameIndexPath];
-  NSString* username = usernameCell.textField.text;
+  return usernameCell.textField.text;
+}
 
+- (NSString*)currentPassword {
   NSIndexPath* passwordIndexPath =
       [NSIndexPath indexPathForRow:NewPasswordTableCellTypePassword
                          inSection:0];
   NewPasswordTableCell* passwordCell =
       [self.tableView cellForRowAtIndexPath:passwordIndexPath];
-  NSString* password = passwordCell.textField.text;
-
-  ArchivableCredential* credential =
-      [self.credentialHandler createNewCredentialWithUsername:username
-                                                     password:password];
-
-  if (!credential) {
-    [self savePasswordFailed];
-    return;
-  }
-
-  [self.credentialHandler
-      saveNewCredential:credential
-             completion:^(NSError* error) {
-               if (error) {
-                 UpdateUMACountForKey(
-                     app_group::kCredentialExtensionSaveCredentialFailureCount);
-                 [self savePasswordFailed];
-                 return;
-               }
-               [self.delegate userSelectedCredential:credential];
-             }];
+  return passwordCell.textField.text;
 }
 
+// Saves the current data as a credential. If |shouldReplace| is YES, then the
+// user has already said they are aware that they are replacing a previous
+// credential.
+- (void)saveCredential:(BOOL)shouldReplace {
+  NSString* username = [self currentUsername];
+  NSString* password = [self currentPassword];
+
+  [self.credentialHandler saveCredentialWithUsername:username
+                                            password:password
+                                       shouldReplace:shouldReplace];
+}
+
+#pragma mark - NewPasswordUIHandler
+
 // Alerts the user that saving their password failed.
-- (void)savePasswordFailed {
-  UIAlertController* ac = [UIAlertController
+- (void)alertSavePasswordFailed {
+  UIAlertController* alertController = [UIAlertController
       alertControllerWithTitle:
           NSLocalizedString(
               @"IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_ERROR_TITLE",
@@ -225,8 +223,45 @@
                 style:UIAlertActionStyleDefault
               handler:^(UIAlertAction* action){
               }];
-  [ac addAction:defaultAction];
-  [self presentViewController:ac animated:YES completion:nil];
+  [alertController addAction:defaultAction];
+  [self presentViewController:alertController animated:YES completion:nil];
+}
+
+- (void)alertUserCredentialExists {
+  NSString* messageBaseLocalizedString = NSLocalizedString(
+      @"IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_MESSAGE",
+      @"Message for password replace alert");
+  NSString* message = [[messageBaseLocalizedString
+      stringByReplacingOccurrencesOfString:@"$2"
+                                withString:self.currentHost]
+      stringByReplacingOccurrencesOfString:@"$1"
+                                withString:[self currentUsername]];
+  UIAlertController* alertController = [UIAlertController
+      alertControllerWithTitle:
+          NSLocalizedString(
+              @"IDS_IOS_CREDENTIAL_PROVIDER_NEW_PASSWORD_REPLACE_TITLE",
+              @"Replace password?")
+                       message:message
+                preferredStyle:UIAlertControllerStyleAlert];
+  __weak __typeof(self) weakSelf = self;
+  UIAlertAction* replaceAction = [UIAlertAction
+      actionWithTitle:NSLocalizedString(
+                          @"IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_REPLACE",
+                          @"Replace")
+                style:UIAlertActionStyleDestructive
+              handler:^(UIAlertAction* action) {
+                [weakSelf saveCredential:YES];
+              }];
+  [alertController addAction:replaceAction];
+  UIAlertAction* cancelAction = [UIAlertAction
+      actionWithTitle:NSLocalizedString(
+                          @"IDS_IOS_CREDENTIAL_PROVIDER_EXTENSION_CANCEL",
+                          @"Cancel")
+                style:UIAlertActionStyleCancel
+              handler:^(UIAlertAction* action){
+              }];
+  [alertController addAction:cancelAction];
+  [self presentViewController:alertController animated:YES completion:nil];
 }
 
 // Returns a new cancel button for the navigation bar.
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index c72e932..626b554 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -394,6 +394,7 @@
     deps += [
       "//ios/chrome/browser/credential_provider:unit_tests",
       "//ios/chrome/credential_provider_extension:unit_tests",
+      "//ios/chrome/credential_provider_extension/ui:unit_tests",
     ]
   }
 
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 6c52c3f..8256117d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-9a9f74880a2673f2adcbf874637370f6b15e9ab8
\ No newline at end of file
+12f4708be935a6b984d46c8c3fd619edfbc06aee
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 0567c112..006385e 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-815b75edb192863fb0f94956d83962a873b9951b
\ No newline at end of file
+c2cb46e66232e23ea738668538a3547c7857b27d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index a1e569c..ab7ed20 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-88f38c115879d393d90904581a28ee6e47152975
\ No newline at end of file
+567791957da493a57959ea40f2b96f49367a8db9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index 57b77ef6..f1ab347 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-5fc70e66b5642178753c755dfa60f52a1b70b70e
\ No newline at end of file
+92483dbbdee94dd2a956ed882fd73e0847271e28
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 8e64914..4de7cc1 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-34a0fa8ab7394c030fd6b404ea1d1c3b64aaa807
\ No newline at end of file
+f0383d133f13228a20e8c48e627d6316cdb27766
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index ea35b648..e86bf91 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-f5d981de4b713bcec6ff469bbe3a2186e3539fb7
\ No newline at end of file
+109be2d79ddea7d3c22be831373e6d0b626752b7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 278f5ae3..72b039df 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-98834484c74c544c05556db15520cb5a754eeb7e
\ No newline at end of file
+7ab19cab3219927743a9364e3647486e0db66780
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 9d0155c0..bed3489 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-53fbf94a5dfb26cfd048f592583557db9b5b883b
\ No newline at end of file
+504c32a942276ab61393426f25a28d972c9e01ea
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 354a16c..7998e0f 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-93e6df7123197059d167a787e9fe1395b522539d
\ No newline at end of file
+baeecd630da45013a4c1b3b383131530d96680b6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index e6dbfd0d..d024350 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-67abcf725ff62ddd00b2f8a3ed1bd793f169226d
\ No newline at end of file
+484973bac940afa16b523f95d3bc758793d3e116
\ No newline at end of file
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 267da9e8d..9b32170 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -206,6 +206,20 @@
 const char kEnableClearHevcForTesting[] = "enable-clear-hevc-for-testing";
 #endif
 
+#if defined(OS_CHROMEOS)
+// These are flags passed from ash-chrome to lacros-chrome that correspond to
+// buildflags for the platform we are running on. lacros-chrome only builds for
+// x86/arm differences, so we unconditionally build in the below features into
+// the relevant parts of lacros-chrome and then filter the functionality based
+// on these command line flags.
+MEDIA_EXPORT extern const char kLacrosEnablePlatformEncryptedHevc[] =
+    "lacros-enable-platform-encrypted-hevc";
+MEDIA_EXPORT extern const char kLacrosEnablePlatformHevc[] =
+    "lacros-enable-platform-hevc";
+MEDIA_EXPORT extern const char kLacrosUseChromeosProtectedMedia[] =
+    "lacros-use-chromeos-protected-media";
+#endif  // defined(OS_CHROMEOS)
+
 namespace autoplay {
 
 // Autoplay policy that requires a document user activation.
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 8b023e8..4e79508 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -91,6 +91,12 @@
 MEDIA_EXPORT extern const char kEnableClearHevcForTesting[];
 #endif
 
+#if defined(OS_CHROMEOS)
+MEDIA_EXPORT extern const char kLacrosEnablePlatformEncryptedHevc[];
+MEDIA_EXPORT extern const char kLacrosEnablePlatformHevc[];
+MEDIA_EXPORT extern const char kLacrosUseChromeosProtectedMedia[];
+#endif  // defined(OS_CHROMEOS)
+
 namespace autoplay {
 
 MEDIA_EXPORT extern const char kDocumentUserActivationRequiredPolicy[];
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 9965a49..336855c8 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -468,25 +468,31 @@
   DVLOG(1) << "Initialize()";
   TRACE_EVENT_ASYNC_BEGIN0("media", "ChunkDemuxer::Initialize", this);
 
-  base::AutoLock auto_lock(lock_);
-  if (state_ == SHUTDOWN) {
-    // Init cb must only be run after this method returns, so post.
-    init_cb_ = BindToCurrentLoop(std::move(init_cb));
-    RunInitCB_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
-    return;
+  base::OnceClosure open_cb;
+
+  // Locked scope
+  {
+    base::AutoLock auto_lock(lock_);
+    if (state_ == SHUTDOWN) {
+      // Init cb must only be run after this method returns, so post.
+      init_cb_ = BindToCurrentLoop(std::move(init_cb));
+      RunInitCB_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
+      return;
+    }
+
+    DCHECK_EQ(state_, WAITING_FOR_INIT);
+    host_ = host;
+    // Do not post init_cb once this function returns because if there is an
+    // error after initialization, the error might be reported before init_cb
+    // has a chance to run. This is because ChunkDemuxer::ReportError_Locked
+    // directly calls DemuxerHost::OnDemuxerError: crbug.com/633016.
+    init_cb_ = std::move(init_cb);
+
+    ChangeState_Locked(INITIALIZING);
+    open_cb = std::move(open_cb_);
   }
 
-  DCHECK_EQ(state_, WAITING_FOR_INIT);
-  host_ = host;
-  // Do not post init_cb once this function returns because if there is an
-  // error after initialization, the error might be reported before init_cb
-  // has a chance to run. This is because ChunkDemuxer::ReportError_Locked
-  // directly calls DemuxerHost::OnDemuxerError: crbug.com/633016.
-  init_cb_ = std::move(init_cb);
-
-  ChangeState_Locked(INITIALIZING);
-
-  std::move(open_cb_).Run();
+  std::move(open_cb).Run();
 }
 
 void ChunkDemuxer::Stop() {
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index c6a847a..4575cf6 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/strings/string_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
@@ -1358,6 +1359,16 @@
   }
 }
 
+TEST_F(ChunkDemuxerTest, AddIdDuringOpenCallback) {
+  // Tests that users may call |ChunkDemuxer::AddId| (or really any method that
+  // acquires |ChunkDemuxer::lock_|) during the open callback.
+  EXPECT_CALL(*this, DemuxerOpened()).WillOnce([this]() { this->AddId(); });
+
+  CreateNewDemuxer();
+  demuxer_->Initialize(&host_, base::DoNothing());
+  ShutdownDemuxer();
+}
+
 TEST_F(ChunkDemuxerTest, AudioVideoTrackIdsChange) {
   // Test with 1 audio and 1 video stream. Send a second init segment in which
   // the audio and video track IDs change. Verify that appended buffers before
diff --git a/media/filters/source_buffer_state.cc b/media/filters/source_buffer_state.cc
index 1ea91df..3a435c0 100644
--- a/media/filters/source_buffer_state.cc
+++ b/media/filters/source_buffer_state.cc
@@ -718,6 +718,15 @@
 
       if (video_config.codec() == kCodecHEVC) {
 #if BUILDFLAG(ENABLE_PLATFORM_ENCRYPTED_HEVC)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+        if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+                switches::kLacrosEnablePlatformEncryptedHevc)) {
+          NOTREACHED() << "MSE parser must not emit HEVC tracks on runtime "
+                          "configurations that do not support HEVC playback "
+                          "via platform.";
+          return false;
+        }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
         // HEVC is only supported through EME under this build flag, so
         // require the config to be for an encrypted track. Even so,
         // conditionally allow clear HEVC if cmdline has test override.
diff --git a/media/gpu/args.gni b/media/gpu/args.gni
index 33ab1fa3..4004937 100644
--- a/media/gpu/args.gni
+++ b/media/gpu/args.gni
@@ -27,6 +27,8 @@
   # Indicates if ChromeOS protected media support exists. This is used
   # to enable the CDM daemon in Chrome OS as well as support for
   # encrypted content with HW video decoders.
+  # TODO(jkardatzke): Enable this for Lacros always, it is determined at runtime
+  # in that configuration.
   use_chromeos_protected_media = false
 
   # Indicates if ChromeOS protected media supports the AV1 codec. By default
diff --git a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
index fff14f9..dfa8b83 100644
--- a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
@@ -218,6 +218,7 @@
         {slice_params_->type(), slice_params_->size(), &slice_param}}};
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   std::unique_ptr<uint8_t[]> protected_vp9_data;
+  std::string amd_decrypt_params;
   if (IsTranscrypted()) {
     CHECK(decrypt_config);
     CHECK_EQ(decrypt_config->subsamples().size(), 1u);
@@ -228,9 +229,19 @@
         return DecodeStatus::kFail;
     }
     DCHECK_EQ(decrypt_config->key_id().length(), protected_params_->size());
+    // For VP9 superframes, the IV may have been incremented, so copy that
+    // back into the decryption parameters. The decryption parameters struct has
+    // a uint32_t for the first parameter, and the second is the 128-bit IV and
+    // then various other fields. Total max structure size is 128 bytes. The
+    // structure definition is in ChromeOS internal code so we do not reference
+    // it directly here.
+    constexpr uint32_t dp_iv_offset = sizeof(uint32_t);
+    amd_decrypt_params = decrypt_config->key_id();
+    memcpy(&amd_decrypt_params[dp_iv_offset], decrypt_config->iv().data(),
+           DecryptConfig::kDecryptionKeySize);
     buffers.push_back({protected_params_->id(),
                        {protected_params_->type(), protected_params_->size(),
-                        decrypt_config->key_id().data()}});
+                        amd_decrypt_params.data()}});
 
     // For transcrypted VP9 on AMD we need to send the UCH + cypher_bytes from
     // the buffer as the slice data per AMD's instructions.
diff --git a/media/mojo/clients/mojo_video_decoder.cc b/media/mojo/clients/mojo_video_decoder.cc
index 2dde9217..f9eb8bc 100644
--- a/media/mojo/clients/mojo_video_decoder.cc
+++ b/media/mojo/clients/mojo_video_decoder.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -134,6 +135,12 @@
   // Currently only the Android backends and specific ChromeOS configurations
   // support decryption.
 #if defined(OS_ANDROID) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosUseChromeosProtectedMedia)) {
+    return false;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   return true;
 #else
   return false;
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn
index d1ead00..e4213fd8 100644
--- a/media/mojo/mojom/BUILD.gn
+++ b/media/mojo/mojom/BUILD.gn
@@ -73,6 +73,7 @@
     "//gpu/ipc/common:interfaces",
     "//media/learning/mojo/public/mojom",
     "//mojo/public/mojom/base",
+    "//sandbox/policy/mojom",
     "//services/media_session/public/mojom",
     "//services/network/public/mojom",
     "//services/network/public/mojom:cookies_mojom",
diff --git a/media/mojo/mojom/cdm_service.mojom b/media/mojo/mojom/cdm_service.mojom
index 49b570aa..cb10ff26f 100644
--- a/media/mojo/mojom/cdm_service.mojom
+++ b/media/mojo/mojom/cdm_service.mojom
@@ -7,6 +7,7 @@
 import "media/mojo/mojom/content_decryption_module.mojom";
 import "media/mojo/mojom/frame_interface_factory.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 import "services/service_manager/public/mojom/interface_provider.mojom";
 
 [EnableIf=is_mac]
@@ -45,6 +46,7 @@
 // - On Mac, the process is fully sandboxed when launched, and `token_provider`
 // is needed to help load the CDM in the process.
 // - On Linux/ChromeOS, the CDM is preloaded in the zygote sandbox.
+[ServiceSandbox=sandbox.mojom.Sandbox.kCdm]
 interface CdmServiceBroker {
   // Loads the CDM at `cdm_path` into the process and returns the `CdmService`.
   GetService(mojo_base.mojom.FilePath cdm_path,
diff --git a/media/mojo/mojom/media_foundation_service.mojom b/media/mojo/mojom/media_foundation_service.mojom
index 941e764..fd7eba5 100644
--- a/media/mojo/mojom/media_foundation_service.mojom
+++ b/media/mojo/mojom/media_foundation_service.mojom
@@ -8,6 +8,7 @@
 import "media/mojo/mojom/interface_factory.mojom";
 import "media/mojo/mojom/key_system_support.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 
 // A service to provide Windows MediaFoundation-based InterfaceFactory and
 // KeySystemCapability. See comments on MediaFoundationServiceBroker for the
@@ -37,6 +38,7 @@
 // be called once and the subsequent calls will simply fail. When
 // `MediaFoundationServiceBroker` is connected the process was not sandboxed to
 // allow CDM preloading. After `GetService()` the process is fully sandboxed.
+[ServiceSandbox=sandbox.mojom.Sandbox.kMediaFoundationCdm]
 interface MediaFoundationServiceBroker {
   // Loads the CDM at `cdm_path` into the process and returns the
   // `MediaFoundationService`.
diff --git a/net/cookies/cookie_partition_key.cc b/net/cookies/cookie_partition_key.cc
index 8d28310..b8b7f1f 100644
--- a/net/cookies/cookie_partition_key.cc
+++ b/net/cookies/cookie_partition_key.cc
@@ -4,6 +4,8 @@
 
 #include "net/cookies/cookie_partition_key.h"
 
+#include "base/feature_list.h"
+#include "net/base/features.h"
 #include "net/cookies/cookie_constants.h"
 
 namespace net {
@@ -65,4 +67,17 @@
   return true;
 }
 
+absl::optional<CookiePartitionKey> CookiePartitionKey::FromNetworkIsolationKey(
+    const NetworkIsolationKey& network_isolation_key) {
+  if (!base::FeatureList::IsEnabled(features::kPartitionedCookies))
+    return absl::nullopt;
+  // TODO(crbug.com/1225444): Check if the top frame site is in a First-Party
+  // Set or if it is an extension URL.
+  absl::optional<net::SchemefulSite> top_frame_site =
+      network_isolation_key.GetTopFrameSite();
+  if (!top_frame_site)
+    return absl::nullopt;
+  return absl::make_optional(net::CookiePartitionKey(top_frame_site.value()));
+}
+
 }  // namespace net
diff --git a/net/cookies/cookie_partition_key.h b/net/cookies/cookie_partition_key.h
index de5580d96..754864a 100644
--- a/net/cookies/cookie_partition_key.h
+++ b/net/cookies/cookie_partition_key.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/schemeful_site.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -63,6 +64,11 @@
     return CookiePartitionKey(url);
   }
 
+  // Create a cookie partition key from a request's NetworkIsolationKey.
+  //
+  static absl::optional<CookiePartitionKey> FromNetworkIsolationKey(
+      const NetworkIsolationKey& network_isolation_key);
+
   // Temporary method, used to mark the places where we need to supply the
   // cookie partition key to CanonicalCookie::Create.
   static absl::optional<CookiePartitionKey> Todo() { return absl::nullopt; }
diff --git a/net/cookies/cookie_partition_key_unittest.cc b/net/cookies/cookie_partition_key_unittest.cc
index ef604ea..7f83ba7 100644
--- a/net/cookies/cookie_partition_key_unittest.cc
+++ b/net/cookies/cookie_partition_key_unittest.cc
@@ -6,12 +6,33 @@
 #define NET_COOKIES_COOKIE_PARTITION_KEY_UNITTEST_H_
 
 #include "net/cookies/cookie_partition_key.h"
+#include "base/test/scoped_feature_list.h"
+#include "net/base/features.h"
 #include "net/cookies/cookie_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 
-TEST(CookiePartitionKeyTest, Serialization) {
+class CookiePartitionKeyTest : public testing::TestWithParam<bool> {
+ protected:
+  // testing::Test
+  void SetUp() override {
+    if (PartitionedCookiesEnabled())
+      scoped_feature_list_.InitAndEnableFeature(features::kPartitionedCookies);
+    testing::TestWithParam<bool>::SetUp();
+  }
+
+  bool PartitionedCookiesEnabled() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(/* no label */,
+                         CookiePartitionKeyTest,
+                         testing::Bool());
+
+TEST_P(CookiePartitionKeyTest, Serialization) {
   struct {
     absl::optional<CookiePartitionKey> input;
     bool expected_ret;
@@ -47,7 +68,7 @@
   }
 }
 
-TEST(CookiePartitionKeyTest, Deserialization) {
+TEST_P(CookiePartitionKeyTest, Deserialization) {
   struct {
     std::string input;
     bool expected_ret;
@@ -72,6 +93,23 @@
   }
 }
 
+TEST_P(CookiePartitionKeyTest, FromNetworkIsolationKey) {
+  EXPECT_FALSE(
+      CookiePartitionKey::FromNetworkIsolationKey(NetworkIsolationKey()));
+
+  SchemefulSite top_level_site =
+      SchemefulSite(GURL("https://toplevelsite.com"));
+  absl::optional<CookiePartitionKey> got =
+      CookiePartitionKey::FromNetworkIsolationKey(NetworkIsolationKey(
+          top_level_site, SchemefulSite(GURL("https://cookiesite.com"))));
+
+  bool partitioned_cookies_enabled = PartitionedCookiesEnabled();
+  EXPECT_EQ(partitioned_cookies_enabled, got.has_value());
+  if (partitioned_cookies_enabled) {
+    EXPECT_EQ(CookiePartitionKey(top_level_site), got.value());
+  }
+}
+
 }  // namespace net
 
 #endif  // NET_COOKIES_COOKIE_PARTITION_KEY_UNITTEST_H_
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 6de416d..a6cd1ef 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -775,7 +775,9 @@
 
     std::unique_ptr<CanonicalCookie> cookie = net::CanonicalCookie::Create(
         request_->url(), cookie_string, base::Time::Now(), server_time,
-        net::CookiePartitionKey::Todo(), &returned_status);
+        net::CookiePartitionKey::FromNetworkIsolationKey(
+            request_->isolation_info().network_isolation_key()),
+        &returned_status);
 
     absl::optional<CanonicalCookie> cookie_to_return = absl::nullopt;
     if (returned_status.IsInclude()) {
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index 6bde63c..cea14f3 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -20,9 +20,11 @@
 #include "base/strings/string_split.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "net/base/auth.h"
+#include "net/base/features.h"
 #include "net/base/isolation_info.h"
 #include "net/base/network_isolation_key.h"
 #include "net/base/request_priority.h"
@@ -1910,4 +1912,73 @@
               MatchesCookieAccessResult(IsInclude(), _, _, _))));
 }
 
+class PartitionedCookiesURLRequestHttpJobTest
+    : public URLRequestHttpJobTest,
+      public testing::WithParamInterface<bool> {
+ protected:
+  // testing::Test
+  void SetUp() override {
+    if (PartitionedCookiesEnabled())
+      scoped_feature_list_.InitAndEnableFeature(features::kPartitionedCookies);
+    URLRequestHttpJobTest::SetUp();
+  }
+
+  bool PartitionedCookiesEnabled() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(/* no label */,
+                         PartitionedCookiesURLRequestHttpJobTest,
+                         testing::Bool());
+
+TEST_P(PartitionedCookiesURLRequestHttpJobTest, SetPartitionedCookie) {
+  EmbeddedTestServer https_test(EmbeddedTestServer::TYPE_HTTPS);
+  const url::Origin kTopFrameOrigin =
+      url::Origin::Create(GURL("https://www.toplevelsite.com"));
+  const IsolationInfo kTestIsolationInfo =
+      IsolationInfo::CreateForInternalRequest(kTopFrameOrigin);
+
+  https_test.AddDefaultHandlers(base::FilePath());
+  ASSERT_TRUE(https_test.Start());
+  TestURLRequestContext context;
+
+  CookieMonster cookie_monster(nullptr, nullptr);
+  context.set_cookie_store(&cookie_monster);
+
+  GURL test_url = https_test.GetURL(
+      "/set-cookie?__Host-foo=bar;SameSite=None;Secure;Path=/;Partitioned;");
+
+  TestDelegate delegate;
+  std::unique_ptr<URLRequest> request = context.CreateRequest(
+      test_url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  request->set_isolation_info(kTestIsolationInfo);
+  request->Start();
+  ASSERT_TRUE(request->is_pending());
+  delegate.RunUntilComplete();
+
+  base::RunLoop run_loop;
+  bool partitioned_cookies_enabled = PartitionedCookiesEnabled();
+  cookie_monster.GetAllCookiesAsync(base::BindLambdaForTesting(
+      [&partitioned_cookies_enabled, &run_loop](const CookieList& cookies) {
+        EXPECT_EQ(1u, cookies.size());
+        EXPECT_EQ(partitioned_cookies_enabled, cookies[0].IsPartitioned());
+        if (partitioned_cookies_enabled) {
+          EXPECT_EQ(CookiePartitionKey::FromURLForTesting(
+                        GURL("https://toplevelsite.com")),
+                    cookies[0].PartitionKey().value());
+        } else {
+          EXPECT_FALSE(cookies[0].PartitionKey());
+        }
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+
+  // TODO(crbug.com/1225444) Test that the cookie is available in a cross-site
+  // context on a different top-level site only when partitioned cookies are
+  // disabled.
+}
+
 }  // namespace net
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 73d20c0..7b4a3578 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -86,7 +86,6 @@
     "//remoting/host/linux:remoting_native_messaging_host",
     "//remoting/host/mac:remoting_native_messaging_host",
     "//remoting/host/mac:remoting_native_messaging_host_executable",
-    "//remoting/host/security_key:main",
     "//remoting/host/win:remoting_console",
     "//remoting/host/win:remoting_core",
     "//remoting/host/win:remoting_desktop",
@@ -99,11 +98,7 @@
 }
 
 source_set("common_headers") {
-  visibility = [
-    ":base",
-    "//remoting/host/security_key:security_key",
-    "//remoting/host/security_key:test_support",
-  ]
+  visibility = [ ":base" ]
   sources = [
     "action_executor.h",
     "audio_capturer.h",
diff --git a/remoting/host/security_key/BUILD.gn b/remoting/host/security_key/BUILD.gn
index 99c97be1..57adea1 100644
--- a/remoting/host/security_key/BUILD.gn
+++ b/remoting/host/security_key/BUILD.gn
@@ -39,7 +39,6 @@
     "//mojo/public/cpp/system",
     "//net:net",
     "//net/traffic_annotation:traffic_annotation",
-    "//remoting/host:common_headers",
     "//remoting/host:host_extension",
     "//remoting/proto",
     "//remoting/protocol:protocol",
@@ -72,7 +71,6 @@
     "//mojo/core/embedder",
     "//remoting/host:base",
     "//remoting/host:common",
-    "//remoting/host:host_main_headers",
   ]
 }
 
@@ -84,7 +82,6 @@
         host_predefines + [ "REMOTING_HOST_BINARY=BINARY_REMOTE_SECURITY_KEY" ]
 
     deps = [
-      ":main",
       "//build/win:default_exe_manifest",
       "//remoting/host/win:remoting_core",
       "//remoting/host/win:remoting_windows_resources",
@@ -150,7 +147,6 @@
     ":security_key",
     "//ipc",
     "//remoting/host:common",
-    "//remoting/host:common_headers",
     "//remoting/proto",
     "//testing/gtest",
   ]
diff --git a/sandbox/policy/mojom/sandbox.mojom b/sandbox/policy/mojom/sandbox.mojom
index 41986b0a..40350448 100644
--- a/sandbox/policy/mojom/sandbox.mojom
+++ b/sandbox/policy/mojom/sandbox.mojom
@@ -21,10 +21,41 @@
   // For instance, it allows dynamic code and wider access to APIs on Windows.
   kUtility,
 
+  // Hosts the content decryption module. Allows pre-loading of CDM libraries.
+  // - On Windows, when `CdmServiceBroker` is connected the CDM was not
+  // sandboxed to allow CDM preloading.
+  // - On Mac, the process is fully sandboxed when launched.
+  // - On Linux/ChromeOS, the CDM is preloaded in the zygote sandbox.
+  kCdm,
+
   // Composits PDF and XPS documents.
   kPrintCompositor,
 
+  // Equivalent to no sandbox on all non-Fuchsia platforms.
+  // Minimally privileged sandbox on Fuchsia.
+  // TODO(crbug.com/1236898) Fix this mapping.
+  kVideoCapture,
+
+  // Allows LPAC capabilities for the Windws Media Foundation CDM, including
+  // internet and private network access, COM, Identity & others. Allows access
+  // to files in the `mediaFoundationCdmFiles` Chromium lpac.
+  [EnableIf=is_win]
+  kMediaFoundationCdm,
+
   // |kXrCompositing| hosts XR Device Service on Windows.
   [EnableIf=is_win]
   kXrCompositing,
+
+  // Hosts Input Method Editors.
+  [EnableIf=is_chromeos_ash]
+  kIme,
+
+  // Text-to-speech.
+  [EnableIf=is_chromeos_ash]
+  kTts,
+
+  // Hosts the Libassistant service on ChromeOS Ash, only used for
+  // Chrome branded builds.
+  [EnableIf=is_chromeos_ash]
+  kLibassistant,
 };
diff --git a/sandbox/policy/sandbox_type.cc b/sandbox/policy/sandbox_type.cc
index c03b072..a0bdc15 100644
--- a/sandbox/policy/sandbox_type.cc
+++ b/sandbox/policy/sandbox_type.cc
@@ -294,6 +294,15 @@
 }
 
 SandboxType UtilitySandboxTypeFromString(const std::string& sandbox_string) {
+  // This function should cover all sandbox types used for utilities, the
+  // CHECK at the end should catch any attempts to forget to add a new type.
+
+  // Most utilities are kUtility or kService so put those first.
+  if (sandbox_string == switches::kUtilitySandbox)
+    return SandboxType::kUtility;
+  if (sandbox_string == switches::kServiceSandbox)
+    return SandboxType::kService;
+
   if (sandbox_string == switches::kNoneSandbox)
     return SandboxType::kNoSandbox;
   if (sandbox_string == switches::kNoneSandboxAndElevatedPrivileges) {
@@ -347,6 +356,10 @@
     return SandboxType::kLibassistant;
 #endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  CHECK(false)
+      << "Command line does not provide a valid sandbox configuration: "
+      << sandbox_string;
+  NOTREACHED();
   return SandboxType::kUtility;
 }
 
diff --git a/sandbox/policy/sandbox_type.h b/sandbox/policy/sandbox_type.h
index 6d4cfd1..8b841e4 100644
--- a/sandbox/policy/sandbox_type.h
+++ b/sandbox/policy/sandbox_type.h
@@ -123,16 +123,36 @@
 inline constexpr sandbox::policy::SandboxType MapToSandboxType(
     sandbox::mojom::Sandbox mojo_sandbox) {
   switch (mojo_sandbox) {
+    case sandbox::mojom::Sandbox::kCdm:
+      return sandbox::policy::SandboxType::kCdm;
     case sandbox::mojom::Sandbox::kPrintCompositor:
       return sandbox::policy::SandboxType::kPrintCompositor;
     case sandbox::mojom::Sandbox::kService:
       return sandbox::policy::SandboxType::kService;
     case sandbox::mojom::Sandbox::kUtility:
       return sandbox::policy::SandboxType::kUtility;
+    case sandbox::mojom::Sandbox::kVideoCapture:
+      return sandbox::policy::SandboxType::kVideoCapture;
 #if defined(OS_WIN)
+    case sandbox::mojom::Sandbox::kMediaFoundationCdm:
+      return sandbox::policy::SandboxType::kMediaFoundationCdm;
     case sandbox::mojom::Sandbox::kXrCompositing:
       return sandbox::policy::SandboxType::kXrCompositing;
 #endif  // OS_WIN
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    case sandbox::mojom::Sandbox::kIme:
+      return sandbox::policy::SandboxType::kIme;
+    case sandbox::mojom::Sandbox::kTts:
+      return sandbox::policy::SandboxType::kTts;
+    case sandbox::mojom::Sandbox::kLibassistant:
+#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
+      return sandbox::policy::SandboxType::kLibassistant;
+#else
+      CHECK(false) << "Libassistant sandbox not supported";
+      NOTREACHED();
+      return sandbox::policy::SandboxType::kService;
+#endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   }
 }
 
diff --git a/sandbox/policy/sandbox_type_unittest.cc b/sandbox/policy/sandbox_type_unittest.cc
index e909a684..44787056 100644
--- a/sandbox/policy/sandbox_type_unittest.cc
+++ b/sandbox/policy/sandbox_type_unittest.cc
@@ -54,7 +54,6 @@
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   command_line.AppendSwitchASCII(switches::kProcessType,
                                  switches::kUtilityProcess);
-  EXPECT_EQ(SandboxType::kUtility, SandboxTypeFromCommandLine(command_line));
 
   base::CommandLine command_line2(command_line);
   SetCommandLineFlagsForSandboxType(&command_line2, SandboxType::kNetwork);
@@ -73,8 +72,8 @@
   EXPECT_EQ(SandboxType::kPpapi, SandboxTypeFromCommandLine(command_line5));
 
   base::CommandLine command_line6(command_line);
-  command_line6.AppendSwitchASCII(switches::kServiceSandboxType, "bogus");
-  EXPECT_EQ(SandboxType::kUtility, SandboxTypeFromCommandLine(command_line6));
+  SetCommandLineFlagsForSandboxType(&command_line6, SandboxType::kService);
+  EXPECT_EQ(SandboxType::kService, SandboxTypeFromCommandLine(command_line6));
 
   base::CommandLine command_line7(command_line);
   SetCommandLineFlagsForSandboxType(&command_line7,
@@ -130,6 +129,24 @@
   EXPECT_EQ(SandboxType::kNoSandbox, SandboxTypeFromCommandLine(command_line));
 }
 
+TEST(SandboxTypeTest, UtilityDeath) {
+  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+  command_line.AppendSwitchASCII(switches::kProcessType,
+                                 switches::kUtilityProcess);
+  EXPECT_DEATH_IF_SUPPORTED(
+      SandboxTypeFromCommandLine(command_line),
+      "Command line does not provide a valid sandbox configuration");
+
+  // kGPU not valid for utility processes.
+  base::CommandLine command_line1(command_line);
+  command_line1.AppendSwitchASCII(switches::kServiceSandboxType, "gpu");
+  EXPECT_DEATH_IF_SUPPORTED(SandboxTypeFromCommandLine(command_line1), "gpu");
+
+  base::CommandLine command_line2(command_line);
+  command_line2.AppendSwitchASCII(switches::kServiceSandboxType, "bogus");
+  EXPECT_DEATH_IF_SUPPORTED(SandboxTypeFromCommandLine(command_line2), "bogus");
+}
+
 TEST(SandboxTypeTest, GPU) {
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   command_line.AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);
diff --git a/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc b/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc
index b298885..7f257c7c 100644
--- a/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc
+++ b/sandbox/policy/win/mf_cdm_sandbox_type_unittest.cc
@@ -11,12 +11,10 @@
 namespace media {
 
 TEST(SandboxTypeTest, Utility) {
-  // Setup to have '--type=utility' first.
+  // Setup to have '--type=utility' first (but no valid sandbox).
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   command_line.AppendSwitchASCII(sandbox::policy::switches::kProcessType,
                                  sandbox::policy::switches::kUtilityProcess);
-  EXPECT_EQ(sandbox::policy::SandboxType::kUtility,
-            sandbox::policy::SandboxTypeFromCommandLine(command_line));
 
   base::CommandLine command_line2(command_line);
   SetCommandLineFlagsForSandboxType(
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 1c5ab77..adf1108 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -5774,7 +5774,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -6035,7 +6035,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index bb0fb27..215279a 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -55945,7 +55945,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -56209,7 +56209,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -56545,7 +56545,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -56806,7 +56806,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -57142,7 +57142,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -57403,7 +57403,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M93",
-              "revision": "version:93.0.4577.33"
+              "revision": "version:93.0.4577.35"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 0f39a99..bc28449 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -1355,6 +1355,10 @@
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
         "name": "cc_unittests_amd64-generic",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -1379,6 +1383,10 @@
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
         "name": "cc_unittests_eve",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -1406,7 +1414,8 @@
         },
         "name": "lacros_all_tast_tests_amd64-generic",
         "resultdb": {
-          "enable": true
+          "enable": true,
+          "has_native_resultdb_integration": true
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -1434,7 +1443,8 @@
         },
         "name": "lacros_all_tast_tests_eve",
         "resultdb": {
-          "enable": true
+          "enable": true,
+          "has_native_resultdb_integration": true
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -1463,6 +1473,10 @@
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
         "name": "ozone_unittests_amd64-generic",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -1487,6 +1501,10 @@
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
         "name": "ozone_unittests_eve",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index ad5e404..156ed274 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -196,7 +196,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -230,7 +230,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -459,7 +459,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -493,7 +493,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -722,7 +722,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -756,7 +756,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -985,7 +985,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -1019,7 +1019,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -1263,7 +1263,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -1299,7 +1299,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -1529,7 +1529,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -1563,7 +1563,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -1812,7 +1812,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -1849,7 +1849,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -2094,7 +2094,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -2130,7 +2130,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -2360,7 +2360,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -2394,7 +2394,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -2620,7 +2620,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -2848,7 +2848,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -2881,7 +2881,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -3103,7 +3103,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -3136,7 +3136,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -3358,7 +3358,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -3391,7 +3391,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -3613,7 +3613,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator",
@@ -3646,7 +3646,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -3870,7 +3870,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator"
@@ -3902,7 +3902,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -4123,7 +4123,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator"
@@ -4155,7 +4155,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -4376,7 +4376,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator"
@@ -4408,7 +4408,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
@@ -4629,7 +4629,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
           "--additional-driver-flag=--enable-dawn-features=use_tint_generator"
@@ -4661,7 +4661,7 @@
       },
       {
         "args": [
-          "--initialize-webgpu-adapter-at-startup",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000",
           "--additional-driver-flag=--enable-dawn-backend-validation=partial",
           "--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis",
           "--additional-driver-flag=--use-gpu-in-tests",
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 6626259..69bfa8d 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -8535,9 +8535,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8561,9 +8561,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8587,9 +8587,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8617,9 +8617,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8645,9 +8645,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8674,9 +8674,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8704,9 +8704,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8732,9 +8732,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8758,9 +8758,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8782,9 +8782,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8803,9 +8803,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8841,9 +8841,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8877,9 +8877,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8913,9 +8913,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8949,9 +8949,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -8989,9 +8989,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9034,9 +9034,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9070,9 +9070,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9115,9 +9115,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9152,9 +9152,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9188,9 +9188,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9226,9 +9226,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -9264,9 +9264,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-440.100",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35781,9 +35781,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35809,9 +35809,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35834,9 +35834,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35860,9 +35860,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35889,9 +35889,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35916,9 +35916,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35941,9 +35941,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35965,9 +35965,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -35991,9 +35991,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36018,9 +36018,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36039,9 +36039,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36060,9 +36060,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36084,9 +36084,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36109,9 +36109,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36147,9 +36147,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36183,9 +36183,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36219,9 +36219,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36255,9 +36255,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36295,9 +36295,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36340,9 +36340,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36385,9 +36385,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36422,9 +36422,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36458,9 +36458,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36496,9 +36496,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36534,9 +36534,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36572,9 +36572,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36609,9 +36609,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36647,9 +36647,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
@@ -36684,9 +36684,9 @@
           "containment_type": "AUTO",
           "dimension_sets": [
             {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
+              "gpu": "10de:2184-27.21.14.5638",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
             }
           ],
           "expiration": 21600,
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 933b430..1a552768 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1453,8 +1453,8 @@
   "performance_web_engine_test_suite": {
     "args": [
       "../../content/test/gpu/run_telemetry_benchmark_fuchsia.py",
-      "--system-log-file",
-      "${ISOLATED_OUTDIR}/system_log",
+      "--system-log-template",
+      "system_log",
     ],
     "label": "//content/test:performance_web_engine_test_suite",
     "script": "//testing/scripts/run_performance_tests.py",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 22d9682e..8df932c7 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -633,9 +633,9 @@
   'linux_nvidia_gtx_1660_experimental': {
     'swarming': {
       'dimensions': {
-        'gpu': '10de:2184',
-        'os': 'Ubuntu',
-        'pool': 'chromium.tests.gpu.experimental',
+        'gpu': '10de:2184-440.100',
+        'os': 'Ubuntu-18.04.5',
+        'pool': 'chromium.tests.gpu',
       },
     },
   },
@@ -1167,9 +1167,9 @@
   'win10_nvidia_geforce_gtx_1660': {
     'swarming': {
       'dimensions': {
-        'gpu': '10de:2184',
-        'os': 'Windows-10',
-        'pool': 'chromium.tests.gpu.experimental',
+        'gpu': '10de:2184-27.21.14.5638',
+        'os': 'Windows-10-18363',
+        'pool': 'chromium.tests.gpu',
       },
     },
   },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 04bcdf2..74ba631 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3946,7 +3946,7 @@
         'name': 'webgpu_blink_web_tests_with_backend_validation',
         'args': [
           # crbug.com/953991 Ensure WebGPU is ready before running tests
-          '--initialize-webgpu-adapter-at-startup',
+          '--initialize-webgpu-adapter-at-startup-timeout-ms=60000',
           '--additional-driver-flag=--enable-dawn-backend-validation',
           # Make Dawn allow "Unsafe APIs" so they can be tested with the WebGPU CTS.
           '--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis',
@@ -3990,7 +3990,7 @@
         'name': 'webgpu_blink_web_tests',
         'args': [
           # crbug.com/953991 Ensure WebGPU is ready before running tests
-          '--initialize-webgpu-adapter-at-startup',
+          '--initialize-webgpu-adapter-at-startup-timeout-ms=60000',
           # Make Dawn allow "Unsafe APIs" so they can be tested with the WebGPU CTS.
           '--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis',
           # We need this flag to initialize ANGLE so that we can explicitly tell
@@ -4033,7 +4033,7 @@
         'name': 'webgpu_blink_web_tests_with_backend_validation',
         'args': [
           # crbug.com/953991 Ensure WebGPU is ready before running tests
-          '--initialize-webgpu-adapter-at-startup',
+          '--initialize-webgpu-adapter-at-startup-timeout-ms=60000',
           '--additional-driver-flag=--enable-dawn-backend-validation=partial',
           # Make Dawn allow "Unsafe APIs" so they can be tested with the WebGPU CTS.
           '--additional-driver-flag=--disable-dawn-features=disallow_unsafe_apis',
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 84699dcf..86c4b8d 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -349,7 +349,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M93',
-          'revision': 'version:93.0.4577.33',
+          'revision': 'version:93.0.4577.35',
         }
       ],
     },
@@ -421,7 +421,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M93',
-          'revision': 'version:93.0.4577.33',
+          'revision': 'version:93.0.4577.35',
         }
       ],
     },
@@ -493,7 +493,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M93',
-          'revision': 'version:93.0.4577.33',
+          'revision': 'version:93.0.4577.35',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index cd5693e..65dabe4e 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1435,6 +1435,9 @@
         'additional_compile_targets': [
           'chrome',
         ],
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
         'test_suites': {
           'gtest_tests': 'lacros_device_or_vm_tests',
         },
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index d2d2e36..9e10ade 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -414,6 +414,7 @@
             # passthrough args must be before reference args and repeat args:
             # crbug.com/928928, crbug.com/894254#c78
             self._get_passthrough_args() +
+            self._generate_syslog_args() +
             self._generate_repeat_args() +
             self._generate_reference_build_args()
            )
@@ -469,6 +470,16 @@
         selection_args.append('--run-abridged-story-set')
     return selection_args
 
+  def _generate_syslog_args(self):
+    if self._options.system_log_template:
+      isolated_out_dir = os.path.dirname(
+          self._options.isolated_script_test_output)
+      return ['--system-log-file', os.path.join(
+          isolated_out_dir,
+          self.benchmark,
+          self._options.system_log_template)]
+    return []
+
 
   def _generate_story_index_ranges(self, sections):
     range_string = ''
@@ -607,6 +618,10 @@
                       help='Comma separated list of benchmark names'
                       ' to run in lieu of indexing into our benchmark bot maps',
                       required=False)
+  # crbug.com/1236245: This allows for per-benchmark device logs.
+  parser.add_argument('--system-log-template',
+                      help='File name template for system logs for each '
+                      'benchmark', required=False)
   # Some executions may have a different sharding scheme and/or set of tests.
   # These files must live in src/tools/perf/core/shard_maps
   parser.add_argument('--test-shard-map-filename', type=str, required=False)
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 47783da5..67e0f6d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5760,6 +5760,25 @@
                     ]
                 },
                 {
+                    "name": "AssistantIntegration_ToolbarMic_20210810",
+                    "params": {
+                        "colorful_mic": "false",
+                        "experiment_id": "47736814",
+                        "min_agsa_version": "12.27"
+                    },
+                    "enable_features": [
+                        "AssistantIntentExperimentId",
+                        "OmniboxAssistantVoiceSearch",
+                        "VoiceButtonInTopToolbar",
+                        "VoiceSearchAudioCapturePolicy"
+                    ],
+                    "disable_features": [
+                        "AssistantIntentPageUrl",
+                        "AssistantIntentTranslateInfo",
+                        "TranslateIntent"
+                    ]
+                },
+                {
                     "name": "AssistantVoiceSearch_DefaultMic",
                     "params": {
                         "colorful_mic": "false",
@@ -6914,16 +6933,23 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "MVP_20210809",
                     "params": {
+                        "default_query_chip": "true",
+                        "default_query_max_width_sp": "115",
                         "language_allowlist": "en",
                         "needs_content": "true",
                         "needs_url": "true",
-                        "stamp": "1Rb"
+                        "stamp": "1Rs"
                     },
                     "enable_features": [
                         "RelatedSearches",
+                        "RelatedSearchesInBar",
                         "RelatedSearchesUi"
+                    ],
+                    "disable_features": [
+                        "RelatedSearchesAlternateUx",
+                        "RelatedSearchesSimplifiedUx"
                     ]
                 }
             ]
diff --git a/third_party/blink/common/switches.cc b/third_party/blink/common/switches.cc
index 688a75e..cde0573 100644
--- a/third_party/blink/common/switches.cc
+++ b/third_party/blink/common/switches.cc
@@ -126,5 +126,10 @@
 // the platform default is used.
 const char kTouchTextSelectionStrategy[] = "touch-selection-strategy";
 
+// Comma-separated list of origins that can use SharedArrayBuffer without
+// enabling cross-origin isolation.
+const char kSharedArrayBufferAllowedOrigins[] =
+    "shared-array-buffer-allowed-origins";
+
 }  // namespace switches
 }  // namespace blink
diff --git a/third_party/blink/public/common/switches.h b/third_party/blink/public/common/switches.h
index 680cb47..6f743b2 100644
--- a/third_party/blink/public/common/switches.h
+++ b/third_party/blink/public/common/switches.h
@@ -46,6 +46,7 @@
 BLINK_COMMON_EXPORT extern const char kShowLayoutShiftRegions[];
 BLINK_COMMON_EXPORT extern const char kShowPaintRects[];
 BLINK_COMMON_EXPORT extern const char kTouchTextSelectionStrategy[];
+BLINK_COMMON_EXPORT extern const char kSharedArrayBufferAllowedOrigins[];
 
 }  // namespace switches
 }  // namespace blink
diff --git a/third_party/blink/public/mojom/clipboard/clipboard.mojom b/third_party/blink/public/mojom/clipboard/clipboard.mojom
index bdc1116..b67ee00a 100644
--- a/third_party/blink/public/mojom/clipboard/clipboard.mojom
+++ b/third_party/blink/public/mojom/clipboard/clipboard.mojom
@@ -67,6 +67,12 @@
 // Failure to do so may lead to users leaking sensitive information,
 // that may for example be held in metadata.
 interface ClipboardHost {
+  // Conservative limits to maximum format name and data sizes, to avoid
+  // potential attacks with long strings. These limits are only applied to
+  // unsanitized custom formats. The size limit includes the null terminator.
+  const uint32 kMaxFormatSize = 1024;
+  const uint32 kMaxDataSize = 1073741824; // 1 GB, or 1 << 30
+
   // Returns a token which uniquely identifies clipboard state.
   // This can be used to version the data on the clipboard and determine
   // whether it has changed.
@@ -122,6 +128,22 @@
   ReadCustomData(ClipboardBuffer buffer, mojo_base.mojom.String16 type) =>
       (mojo_base.mojom.BigString16 result);
 
+  // Reads both unsanitized custom formats & standard
+  // formats(such as text/html).
+  // `format_types` contains the list of all available custom & standard
+  // formats.
+  [Sync]
+  ReadAvailableCustomAndStandardFormats() =>
+      (array<mojo_base.mojom.String16> format_types);
+
+  // Reads an unsanitized custom format from the clipboard.
+  // `format` contains the custom format name.
+  // Returns the data from the clipboard corresponding to the `format`.
+  // Returns empty `data` if the `format` is not available in the clipboard.
+  [Sync]
+  ReadUnsanitizedCustomFormat(mojo_base.mojom.String16 format) =>
+      (mojo_base.mojom.BigBuffer data);
+
   // Writing to the clipboard via mojo is a two-phase operation. First, the
   // sender sends the different types of data it'd like to write to the
   // receiver. Then, it sends a commit message to commit the data to the system
@@ -148,6 +170,14 @@
 
   WriteImage(skia.mojom.BitmapN32 image);
 
+  // Writes an unsanitized custom format to the clipboard.
+  // `format` contains the name of the custom format and `data` contains the
+  // unsanitized payload provided by the web authors.
+  // There can only be 100 custom formats per write operation and it will be
+  // registered when the web authors request for a custom format.
+  WriteUnsanitizedCustomFormat(mojo_base.mojom.String16 format,
+                               mojo_base.mojom.BigBuffer data);
+
   CommitWrite();
 
   [EnableIf=is_mac]
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.cc b/third_party/blink/renderer/bindings/core/v8/module_record.cc
index 191b1168..9bf79fe 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.cc
@@ -83,7 +83,8 @@
 
   if (!V8ScriptRunner::CompileModule(
            isolate, params, text_position, compile_options, no_cache_reason,
-           ReferrerScriptInfo(params.BaseURL(), options))
+           ReferrerScriptInfo::CreateWithReferencingScript(params.BaseURL(),
+                                                           options))
            .ToLocal(&module)) {
     DCHECK(try_catch.HasCaught());
     exception_state.RethrowV8Exception(try_catch.Exception());
diff --git a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
index 53f444e2..035361f 100644
--- a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
+++ b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
@@ -21,30 +21,31 @@
   kLength
 };
 
-// Omit storing base URL if it is same as ScriptOrigin::ResourceName().
-// Note: This improves chance of getting into a fast path in
-//       ReferrerScriptInfo::ToV8HostDefinedOptions.
-KURL GetStoredBaseUrl(const ReferrerScriptInfo& referrer_info,
-                      const KURL& script_origin_resource_name) {
-  if (referrer_info.BaseURL() == script_origin_resource_name)
-    return KURL();
-
-  // TODO(https://crbug.com/1235202): Currently when either `base_url_` is
-  // `script_origin_resource_name` or null URL, they both result in
-  // `script_origin_resource_name` in FromV8HostDefinedOptions(). Subsequent
-  // CLs will fix this issue.
-  if (referrer_info.BaseURL().IsNull())
-    return KURL();
-
-  return referrer_info.BaseURL();
-}
+static_assert(HostDefinedOptionsIndex::kLength != 1,
+              "We use an array of length 1 to represent ReferrerScriptInfo "
+              "with default value, kLength should be different from 1");
 
 }  // namespace
 
-bool ReferrerScriptInfo::IsDefaultValue(
+ReferrerScriptInfo ReferrerScriptInfo::CreateNoReferencingScript() {
+  return ReferrerScriptInfo();
+}
+
+ReferrerScriptInfo ReferrerScriptInfo::CreateWithReferencingScript(
+    const KURL& base_url,
+    const ScriptFetchOptions& options) {
+  return ReferrerScriptInfo(base_url, options.CredentialsMode(),
+                            options.Nonce(), options.ParserState(),
+                            options.GetReferrerPolicy());
+}
+
+bool ReferrerScriptInfo::HasReferencingScriptWithDefaultValue(
     const KURL& script_origin_resource_name) const {
+  if (!HasReferencingScript())
+    return false;
+
   // TODO(https://crbug.com/1235205): `referrer_policy_` should be checked.
-  return GetStoredBaseUrl(*this, script_origin_resource_name).IsNull() &&
+  return BaseURL() == script_origin_resource_name &&
          credentials_mode_ == network::mojom::CredentialsMode::kSameOrigin &&
          nonce_.IsEmpty() && parser_state_ == kNotParserInserted;
 }
@@ -53,14 +54,15 @@
     v8::Local<v8::Context> context,
     v8::Local<v8::PrimitiveArray> host_defined_options,
     const KURL& script_origin_resource_name) {
-  if (host_defined_options.IsEmpty() || !host_defined_options->Length()) {
-    // Default value. As base URL is null, defer to
-    // `script_origin_resource_name`.
-    ReferrerScriptInfo referrer_info(
-        script_origin_resource_name,
-        network::mojom::CredentialsMode::kSameOrigin, String(),
-        kNotParserInserted, network::mojom::ReferrerPolicy::kDefault);
-    DCHECK(referrer_info.IsDefaultValue(script_origin_resource_name));
+  if (host_defined_options.IsEmpty() || !host_defined_options->Length())
+    return ReferrerScriptInfo::CreateNoReferencingScript();
+
+  if (host_defined_options->Length() == 1) {
+    ReferrerScriptInfo referrer_info =
+        ReferrerScriptInfo::CreateWithReferencingScript(
+            script_origin_resource_name, ScriptFetchOptions());
+    DCHECK(referrer_info.HasReferencingScriptWithDefaultValue(
+        script_origin_resource_name));
     return referrer_info;
   }
 
@@ -72,10 +74,8 @@
       ToCoreString(v8::Local<v8::String>::Cast(base_url_value));
   KURL base_url = base_url_string.IsEmpty() ? KURL() : KURL(base_url_string);
   DCHECK(base_url.IsNull() || base_url.IsValid());
-  if (base_url.IsNull()) {
-    // If base URL is null, defer to `script_origin_resource_name`.
-    base_url = script_origin_resource_name;
-  }
+  // Even if base URL is null, do not defer to `script_origin_resource_name`,
+  // because there are cases where the original base URL is already null.
 
   v8::Local<v8::Primitive> credentials_mode_value =
       host_defined_options->Get(isolate, kCredentialsMode);
@@ -111,15 +111,18 @@
 v8::Local<v8::PrimitiveArray> ReferrerScriptInfo::ToV8HostDefinedOptions(
     v8::Isolate* isolate,
     const KURL& script_origin_resource_name) const {
-  if (IsDefaultValue(script_origin_resource_name))
+  if (!HasReferencingScript())
     return v8::Local<v8::PrimitiveArray>();
 
+  if (HasReferencingScriptWithDefaultValue(script_origin_resource_name))
+    return v8::PrimitiveArray::New(isolate, 1);
+
   v8::Local<v8::PrimitiveArray> host_defined_options =
       v8::PrimitiveArray::New(isolate, HostDefinedOptionsIndex::kLength);
 
-  const KURL stored_base_url =
-      GetStoredBaseUrl(*this, script_origin_resource_name);
-
+  // Even when the original `base_url_` is `script_origin_resource_name`, the
+  // original `base_url_` is stored here, to ditinguish `base_url_` ==
+  // `script_origin_resource_name` and `base_url_` is null.
   v8::Local<v8::Primitive> base_url_value =
       V8String(isolate, base_url_.GetString());
   host_defined_options->Set(isolate, HostDefinedOptionsIndex::kBaseURL,
diff --git a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h
index b8635162..4d34241 100644
--- a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h
+++ b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.h
@@ -18,28 +18,36 @@
 
 // ReferrerScriptInfo carries a copy of "referencing script's" info referenced
 // in HTML Spec: "HostImportModuleDynamically" algorithm.
-// https://html.spec.whatwg.org/C/#hostimportmoduledynamically(referencingscriptormodule,-specifier,-promisecapability)
+// https://html.spec.whatwg.org/C/#hostimportmoduledynamically(referencingscriptormodule,-modulerequest,-promisecapability)
+
+// There are three sub cases for a referencing script:
+//
+// 1. No referencing scripts (e.g. event handlers)
+//    - CreateNoReferencingScript().
+//    - ReferrerScriptInfo::HasReferencingScript() is false.
+//    - V8's HostDefinedOption is empty `v8::Local<v8::PrimitiveArray>()`.
+//
+// 2. A referencing script with default value (e.g. many of classic scripts)
+//    - CreateWithReferencingScript() with the base URL ==
+//    ScriptOrigin::ResourceName() and default `ScriptFetchOptions()`.
+//    - ReferrerScriptInfo::HasReferencingScript() is true.
+//    - V8's HostDefinedOption is `v8::PrimitiveArray` with length 1.
+//
+// 3. A referencing script with non-default value
+//    - CreateWithReferencingScript().
+//    - ReferrerScriptInfo::HasReferencingScript() is true.
+//    - V8's HostDefinedOption is `v8::PrimitiveArray` with length
+//    HostDefinedOptionsIndex::kLength
 class CORE_EXPORT ReferrerScriptInfo {
   STACK_ALLOCATED();
 
  public:
-  ReferrerScriptInfo() {}
-  ReferrerScriptInfo(const KURL& base_url,
-                     network::mojom::CredentialsMode credentials_mode,
-                     const String& nonce,
-                     ParserDisposition parser_state,
-                     network::mojom::ReferrerPolicy referrer_policy)
-      : base_url_(base_url),
-        credentials_mode_(credentials_mode),
-        nonce_(nonce),
-        parser_state_(parser_state),
-        referrer_policy_(referrer_policy) {}
-  ReferrerScriptInfo(const KURL& base_url, const ScriptFetchOptions& options)
-      : ReferrerScriptInfo(base_url,
-                           options.CredentialsMode(),
-                           options.Nonce(),
-                           options.ParserState(),
-                           options.GetReferrerPolicy()) {}
+  static ReferrerScriptInfo CreateNoReferencingScript();
+
+  // There should exist a corresponding `blink::Script`.
+  static ReferrerScriptInfo CreateWithReferencingScript(
+      const KURL& base_url,
+      const ScriptFetchOptions&);
 
   static ReferrerScriptInfo FromV8HostDefinedOptions(
       v8::Local<v8::Context>,
@@ -49,6 +57,9 @@
       v8::Isolate*,
       const KURL& script_origin_resource_name) const;
 
+  bool HasReferencingScript() const { return has_referencing_script_; }
+  bool HasReferencingScriptWithDefaultValue(
+      const KURL& script_origin_resource_name) const;
   const KURL& BaseURL() const { return base_url_; }
   network::mojom::CredentialsMode CredentialsMode() const {
     return credentials_mode_;
@@ -59,12 +70,26 @@
     return referrer_policy_;
   }
 
-  bool IsDefaultValue(const KURL& script_origin_resource_name) const;
-
  private:
+  ReferrerScriptInfo() = default;
+  ReferrerScriptInfo(const KURL& base_url,
+                     network::mojom::CredentialsMode credentials_mode,
+                     const String& nonce,
+                     ParserDisposition parser_state,
+                     network::mojom::ReferrerPolicy referrer_policy)
+      : has_referencing_script_(true),
+        base_url_(base_url),
+        credentials_mode_(credentials_mode),
+        nonce_(nonce),
+        parser_state_(parser_state),
+        referrer_policy_(referrer_policy) {}
+
+  // Spec: referencingScriptOrModule is not null.
+  const bool has_referencing_script_ = false;
+
   // Spec: "referencing script's base URL"
   // https://html.spec.whatwg.org/C/#concept-script-base-url
-  const KURL base_url_;
+  const KURL base_url_ = KURL();
 
   // Spec: "referencing script's credentials mode"
   // The default value is "same-origin" per:
@@ -73,7 +98,7 @@
       network::mojom::CredentialsMode::kSameOrigin;
 
   // Spec: "referencing script's cryptographic nonce"
-  const String nonce_;
+  const String nonce_ = String();
 
   // Spec: "referencing script's parser state"
   // The default value is "not-parser-inserted" per:
diff --git a/third_party/blink/renderer/bindings/core/v8/referrer_script_info_test.cc b/third_party/blink/renderer/bindings/core/v8/referrer_script_info_test.cc
index fe4050b4..6141c76 100644
--- a/third_party/blink/renderer/bindings/core/v8/referrer_script_info_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/referrer_script_info_test.cc
@@ -10,25 +10,43 @@
 
 namespace blink {
 
-TEST(ReferrerScriptInfo, IsDefaultValue) {
+TEST(ReferrerScriptInfo, HasReferencingScriptWithDefaultValue) {
   const KURL script_origin_resource_name("http://example.org/script.js");
 
-  // TODO(https://crbug.com/1114993): There three cases should be distinguished.
-  EXPECT_TRUE(ReferrerScriptInfo().IsDefaultValue(script_origin_resource_name));
-  EXPECT_TRUE(
-      ReferrerScriptInfo(script_origin_resource_name, ScriptFetchOptions())
-          .IsDefaultValue(script_origin_resource_name));
-  EXPECT_TRUE(ReferrerScriptInfo(KURL(), ScriptFetchOptions())
-                  .IsDefaultValue(script_origin_resource_name));
+  auto info_no_script = ReferrerScriptInfo::CreateNoReferencingScript();
+  EXPECT_FALSE(info_no_script.HasReferencingScript());
+  EXPECT_FALSE(info_no_script.HasReferencingScriptWithDefaultValue(
+      script_origin_resource_name));
 
-  EXPECT_FALSE(
-      ReferrerScriptInfo(KURL("http://example.com"), ScriptFetchOptions())
-          .IsDefaultValue(script_origin_resource_name));
-  EXPECT_FALSE(ReferrerScriptInfo(KURL("http://example.com"),
-                                  network::mojom::CredentialsMode::kInclude, "",
-                                  kNotParserInserted,
-                                  network::mojom::ReferrerPolicy::kDefault)
-                   .IsDefaultValue(script_origin_resource_name));
+  auto info_default_script = ReferrerScriptInfo::CreateWithReferencingScript(
+      script_origin_resource_name, ScriptFetchOptions());
+  EXPECT_TRUE(info_default_script.HasReferencingScript());
+  EXPECT_TRUE(info_default_script.HasReferencingScriptWithDefaultValue(
+      script_origin_resource_name));
+
+  auto info_null_url_script = ReferrerScriptInfo::CreateWithReferencingScript(
+      KURL(), ScriptFetchOptions());
+  EXPECT_TRUE(info_null_url_script.HasReferencingScript());
+  EXPECT_FALSE(info_null_url_script.HasReferencingScriptWithDefaultValue(
+      script_origin_resource_name));
+
+  auto info_script = ReferrerScriptInfo::CreateWithReferencingScript(
+      KURL("http://example.com"), ScriptFetchOptions());
+  EXPECT_TRUE(info_script.HasReferencingScript());
+  EXPECT_FALSE(info_script.HasReferencingScriptWithDefaultValue(
+      script_origin_resource_name));
+
+  auto info_nondefault_options =
+      ReferrerScriptInfo::CreateWithReferencingScript(
+          KURL("http://example.com"),
+          ScriptFetchOptions("", {}, {}, kNotParserInserted,
+                             network::mojom::CredentialsMode::kInclude,
+                             network::mojom::ReferrerPolicy::kDefault,
+                             mojom::FetchImportanceMode::kImportanceAuto,
+                             RenderBlockingBehavior::kUnset));
+  EXPECT_TRUE(info_nondefault_options.HasReferencingScript());
+  EXPECT_FALSE(info_nondefault_options.HasReferencingScriptWithDefaultValue(
+      script_origin_resource_name));
 }
 
 TEST(ReferrerScriptInfo, ToFromV8NoReferencingScript) {
@@ -36,16 +54,15 @@
   const KURL script_origin_resource_name("http://example.org/script.js");
 
   v8::Local<v8::PrimitiveArray> v8_info =
-      ReferrerScriptInfo().ToV8HostDefinedOptions(scope.GetIsolate(),
-                                                  script_origin_resource_name);
+      ReferrerScriptInfo::CreateNoReferencingScript().ToV8HostDefinedOptions(
+          scope.GetIsolate(), script_origin_resource_name);
 
   EXPECT_TRUE(v8_info.IsEmpty());
 
   ReferrerScriptInfo decoded = ReferrerScriptInfo::FromV8HostDefinedOptions(
       scope.GetContext(), v8_info, script_origin_resource_name);
 
-  // TODO(https://crbug.com/1235202): This should be null URL.
-  EXPECT_EQ(script_origin_resource_name, decoded.BaseURL());
+  EXPECT_FALSE(decoded.HasReferencingScript());
 }
 
 TEST(ReferrerScriptInfo, ToFromV8ScriptOriginBaseUrl) {
@@ -53,15 +70,18 @@
   const KURL script_origin_resource_name("http://example.org/script.js");
 
   v8::Local<v8::PrimitiveArray> v8_info =
-      ReferrerScriptInfo(script_origin_resource_name, ScriptFetchOptions())
+      ReferrerScriptInfo::CreateWithReferencingScript(
+          script_origin_resource_name, ScriptFetchOptions())
           .ToV8HostDefinedOptions(scope.GetIsolate(),
                                   script_origin_resource_name);
 
-  EXPECT_TRUE(v8_info.IsEmpty());
+  ASSERT_FALSE(v8_info.IsEmpty());
+  EXPECT_EQ(v8_info->Length(), 1);
 
   ReferrerScriptInfo decoded = ReferrerScriptInfo::FromV8HostDefinedOptions(
       scope.GetContext(), v8_info, script_origin_resource_name);
 
+  EXPECT_TRUE(decoded.HasReferencingScript());
   EXPECT_EQ(script_origin_resource_name, decoded.BaseURL());
 }
 
@@ -70,17 +90,19 @@
   const KURL script_origin_resource_name("http://example.org/script.js");
 
   v8::Local<v8::PrimitiveArray> v8_info =
-      ReferrerScriptInfo(KURL(), ScriptFetchOptions())
+      ReferrerScriptInfo::CreateWithReferencingScript(KURL(),
+                                                      ScriptFetchOptions())
           .ToV8HostDefinedOptions(scope.GetIsolate(),
                                   script_origin_resource_name);
 
-  EXPECT_TRUE(v8_info.IsEmpty());
+  ASSERT_FALSE(v8_info.IsEmpty());
+  EXPECT_GT(v8_info->Length(), 1);
 
   ReferrerScriptInfo decoded = ReferrerScriptInfo::FromV8HostDefinedOptions(
       scope.GetContext(), v8_info, script_origin_resource_name);
 
-  // TODO(https://crbug.com/1235202): This should be null URL.
-  EXPECT_EQ(script_origin_resource_name, decoded.BaseURL());
+  EXPECT_TRUE(decoded.HasReferencingScript());
+  EXPECT_EQ(KURL(), decoded.BaseURL());
 }
 
 TEST(ReferrerScriptInfo, ToFromV8) {
@@ -88,14 +110,23 @@
   const KURL script_origin_resource_name("http://example.org/script.js");
   const KURL url("http://example.com");
 
-  ReferrerScriptInfo info(url, network::mojom::CredentialsMode::kInclude,
-                          "foobar", kNotParserInserted,
-                          network::mojom::ReferrerPolicy::kOrigin);
+  auto info = ReferrerScriptInfo::CreateWithReferencingScript(
+      url, ScriptFetchOptions("foobar", {}, {}, kNotParserInserted,
+                              network::mojom::CredentialsMode::kInclude,
+                              network::mojom::ReferrerPolicy::kOrigin,
+                              mojom::FetchImportanceMode::kImportanceAuto,
+                              RenderBlockingBehavior::kUnset));
+
   v8::Local<v8::PrimitiveArray> v8_info = info.ToV8HostDefinedOptions(
       scope.GetIsolate(), script_origin_resource_name);
 
+  ASSERT_FALSE(v8_info.IsEmpty());
+  EXPECT_GT(v8_info->Length(), 1);
+
   ReferrerScriptInfo decoded = ReferrerScriptInfo::FromV8HostDefinedOptions(
       scope.GetContext(), v8_info, script_origin_resource_name);
+
+  EXPECT_TRUE(decoded.HasReferencingScript());
   EXPECT_EQ(url, decoded.BaseURL());
   EXPECT_EQ(network::mojom::CredentialsMode::kInclude,
             decoded.CredentialsMode());
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index ac74f1d..84de63b 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -251,7 +251,8 @@
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
                   scope.GetScriptState(), source_code,
                   SanitizeScriptErrors::kDoNotSanitize, compile_options,
-                  no_cache_reason, ReferrerScriptInfo())
+                  no_cache_reason,
+                  ReferrerScriptInfo::CreateNoReferencingScript())
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
@@ -288,7 +289,8 @@
   EXPECT_FALSE(V8ScriptRunner::CompileScript(
                    scope.GetScriptState(), source_code,
                    SanitizeScriptErrors::kDoNotSanitize, compile_options,
-                   no_cache_reason, ReferrerScriptInfo())
+                   no_cache_reason,
+                   ReferrerScriptInfo::CreateNoReferencingScript())
                    .ToLocal(&script));
   EXPECT_TRUE(try_catch.HasCaught());
 }
@@ -443,7 +445,8 @@
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
                   scope.GetScriptState(), source_code,
                   SanitizeScriptErrors::kDoNotSanitize, compile_options,
-                  no_cache_reason, ReferrerScriptInfo())
+                  no_cache_reason,
+                  ReferrerScriptInfo::CreateNoReferencingScript())
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
@@ -479,7 +482,8 @@
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
                   scope.GetScriptState(), source_code,
                   SanitizeScriptErrors::kDoNotSanitize, compile_options,
-                  no_cache_reason, ReferrerScriptInfo())
+                  no_cache_reason,
+                  ReferrerScriptInfo::CreateNoReferencingScript())
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
@@ -516,7 +520,8 @@
   EXPECT_TRUE(V8ScriptRunner::CompileScript(
                   scope.GetScriptState(), source_code,
                   SanitizeScriptErrors::kDoNotSanitize, compile_options,
-                  no_cache_reason, ReferrerScriptInfo())
+                  no_cache_reason,
+                  ReferrerScriptInfo::CreateNoReferencingScript())
                   .ToLocal(&script));
   EXPECT_FALSE(try_catch.HasCaught());
 }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc b/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc
index 0b04fe61..10657ec 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_code_cache.cc
@@ -329,7 +329,7 @@
   // v8::TryCatch is needed to suppress all exceptions thrown during the code
   // cache generation.
   v8::TryCatch block(isolate);
-  ReferrerScriptInfo referrer_info;
+  auto referrer_info = ReferrerScriptInfo::CreateNoReferencingScript();
   v8::ScriptOrigin origin(
       isolate, V8String(isolate, file_name),
       0,                                      // line_offset
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index 67f0f5d..6089263d 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -472,8 +472,9 @@
       try_catch.SetVerbose(true);
     }
 
-    const ReferrerScriptInfo referrer_info(classic_script->BaseURL(),
-                                           classic_script->FetchOptions());
+    const ReferrerScriptInfo referrer_info =
+        ReferrerScriptInfo::CreateWithReferencingScript(
+            classic_script->BaseURL(), classic_script->FetchOptions());
 
     v8::Local<v8::Script> script;
 
@@ -594,7 +595,8 @@
   // - parser_state: always "not parser inserted" for internal scripts.
   if (!V8ScriptRunner::CompileScript(
            script_state, source_code, SanitizeScriptErrors::kDoNotSanitize,
-           compile_options, no_cache_reason, ReferrerScriptInfo())
+           compile_options, no_cache_reason,
+           ReferrerScriptInfo::CreateNoReferencingScript())
            .ToLocal(&script))
     return v8::MaybeLocal<v8::Value>();
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
index 6c6b416..491b5f0 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
@@ -85,7 +85,8 @@
         V8CodeCache::GetCompileOptions(cache_options, source_code);
     v8::MaybeLocal<v8::Script> compiled_script = V8ScriptRunner::CompileScript(
         script_state, source_code, SanitizeScriptErrors::kSanitize,
-        compile_options, no_cache_reason, ReferrerScriptInfo());
+        compile_options, no_cache_reason,
+        ReferrerScriptInfo::CreateNoReferencingScript());
     if (compiled_script.IsEmpty()) {
       return false;
     }
@@ -110,7 +111,8 @@
     }
     v8::MaybeLocal<v8::Script> compiled_script = V8ScriptRunner::CompileScript(
         script_state, source_code, SanitizeScriptErrors::kSanitize,
-        compile_options, no_cache_reason, ReferrerScriptInfo());
+        compile_options, no_cache_reason,
+        ReferrerScriptInfo::CreateNoReferencingScript());
     if (compiled_script.IsEmpty()) {
       return false;
     }
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index ca11b00..f3b9ef4c 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/audio_data.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame_transfer_list.h"
@@ -1126,7 +1127,8 @@
   // Copy out the frames to make sure they haven't been changed during the
   // transfer.
   DOMArrayBuffer* copy_dest = DOMArrayBuffer::Create(kFrames, sizeof(float));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(copy_dest);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(copy_dest);
   AudioDataCopyToOptions* options =
       MakeGarbageCollected<AudioDataCopyToOptions>();
 
diff --git a/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
index a1c07b9..e9cc73a 100644
--- a/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/element_factory.cc.tmpl
@@ -66,7 +66,7 @@
     const CreateElementFlags flags) {
   if (!g_{{namespace|lower}}_constructors)
     Create{{namespace}}FunctionMap();
-  if ({{namespace}}ConstructorFunction function = g_{{namespace|lower}}_constructors->at(local_name))
+  if ({{namespace}}ConstructorFunction function = g_{{namespace|lower}}_constructors->DeprecatedAtOrEmptyValue(local_name))
     return function(document, flags);
   return nullptr;
 }
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 318e550f..3fc054a 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1449,6 +1449,7 @@
     "loader/resource/mock_image_resource_observer.cc",
     "loader/resource/mock_image_resource_observer.h",
     "loader/resource/multipart_image_resource_parser_test.cc",
+    "loader/resource/resource_loader_code_cache_test.cc",
     "loader/resource/script_resource_test.cc",
     "loader/resource_load_observer_for_frame_test.cc",
     "loader/threadable_loader_test.cc",
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.cc b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
index 66b07e3..dfe5ebe 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.cc
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
@@ -370,6 +370,29 @@
   }
 }
 
+void SystemClipboard::ReadAvailableCustomAndStandardFormats(
+    mojom::blink::ClipboardHost::ReadAvailableCustomAndStandardFormatsCallback
+        callback) {
+  clipboard_->ReadAvailableCustomAndStandardFormats(std::move(callback));
+}
+
+void SystemClipboard::ReadUnsanitizedCustomFormat(
+    const String& type,
+    mojom::blink::ClipboardHost::ReadUnsanitizedCustomFormatCallback callback) {
+  // The format size restriction is added in `ClipboardWriter::IsValidType`.
+  DCHECK_LT(type.length(), mojom::blink::ClipboardHost::kMaxFormatSize);
+  clipboard_->ReadUnsanitizedCustomFormat(type, std::move(callback));
+}
+
+void SystemClipboard::WriteUnsanitizedCustomFormat(const String& type,
+                                                   mojo_base::BigBuffer data) {
+  if (data.size() >= mojom::blink::ClipboardHost::kMaxDataSize)
+    return;
+  // The format size restriction is added in `ClipboardWriter::IsValidType`.
+  DCHECK_LT(type.length(), mojom::blink::ClipboardHost::kMaxFormatSize);
+  clipboard_->WriteUnsanitizedCustomFormat(type, std::move(data));
+}
+
 void SystemClipboard::Trace(Visitor* visitor) const {
   visitor->Trace(clipboard_);
 }
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.h b/third_party/blink/renderer/core/clipboard/system_clipboard.h
index 38a0a8fa..b381f12a 100644
--- a/third_party/blink/renderer/core/clipboard/system_clipboard.h
+++ b/third_party/blink/renderer/core/clipboard/system_clipboard.h
@@ -86,6 +86,17 @@
   void RecordClipboardImageUrls(DocumentFragment* pasting_fragment);
   void RecordImageLoadError(const String& image_url);
 
+  void ReadAvailableCustomAndStandardFormats(
+      mojom::blink::ClipboardHost::ReadAvailableCustomAndStandardFormatsCallback
+          callback);
+  void ReadUnsanitizedCustomFormat(
+      const String& type,
+      mojom::blink::ClipboardHost::ReadUnsanitizedCustomFormatCallback
+          callback);
+
+  void WriteUnsanitizedCustomFormat(const String& type,
+                                    mojo_base::BigBuffer data);
+
   void Trace(Visitor*) const;
 
  private:
diff --git a/third_party/blink/renderer/core/css/resolver/font_builder.cc b/third_party/blink/renderer/core/css/resolver/font_builder.cc
index d427f22f..2fa0a3e 100644
--- a/third_party/blink/renderer/core/css/resolver/font_builder.cc
+++ b/third_party/blink/renderer/core/css/resolver/font_builder.cc
@@ -217,7 +217,7 @@
 
   bool is_initial =
       family_description.generic_family == FontDescription::kStandardFamily &&
-      family_description.family.FamilyIsEmpty();
+      family_description.family.Family().IsEmpty();
 
   font_description.SetGenericFamily(family_description.generic_family);
   font_description.SetFamily(is_initial ? StandardFontFamily()
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index 20fa8ff4..f079c9061 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -219,6 +219,11 @@
     return true;
   }
 
+  if (SecurityPolicy::IsSharedArrayBufferAlwaysAllowedForOrigin(
+          GetSecurityOrigin())) {
+    return true;
+  }
+
 #if defined(OS_ANDROID)
   return false;
 #else
diff --git a/third_party/blink/renderer/core/frame/frame_overlay_test.cc b/third_party/blink/renderer/core/frame/frame_overlay_test.cc
index e8c4618..f32836f 100644
--- a/third_party/blink/renderer/core/frame/frame_overlay_test.cc
+++ b/third_party/blink/renderer/core/frame/frame_overlay_test.cc
@@ -115,9 +115,10 @@
     EXPECT_EQ(PropertyTreeState::Root(),
               graphics_layer->GetPropertyTreeState());
     Vector<PreCompositedLayerInfo> pre_composited_layers;
-    graphics_layer->PaintRecursively(builder.Context(), pre_composited_layers);
+    PaintController::CycleScope cycle_scope;
+    graphics_layer->PaintRecursively(builder.Context(), pre_composited_layers,
+                                     cycle_scope);
     ASSERT_EQ(1u, pre_composited_layers.size());
-    graphics_layer->GetPaintController().FinishCycle();
     SkiaPaintCanvas(&canvas).drawPicture(
         graphics_layer->GetPaintController().GetPaintArtifact().GetPaintRecord(
             PropertyTreeState::Root()));
@@ -171,9 +172,10 @@
     EXPECT_FALSE(graphics_layer->IsHitTestable());
     EXPECT_EQ(state, graphics_layer->GetPropertyTreeState());
     Vector<PreCompositedLayerInfo> pre_composited_layers;
-    graphics_layer->PaintRecursively(context, pre_composited_layers);
+    PaintController::CycleScope cycle_scope;
+    graphics_layer->PaintRecursively(context, pre_composited_layers,
+                                     cycle_scope);
     check_paint_results(graphics_layer->GetPaintController());
-    graphics_layer->GetPaintController().FinishCycle();
   }
 }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index cf18885..9e72317 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2751,17 +2751,21 @@
     }
   });
 
-  bool repainted = PaintTree(benchmark_mode);
+  bool needed_update;
+  {
+    PaintController::CycleScope cycle_scope;
+    bool repainted = PaintTree(benchmark_mode, cycle_scope);
 
-  if (paint_artifact_compositor_ &&
-      benchmark_mode ==
-          PaintBenchmarkMode::kForcePaintArtifactCompositorUpdate) {
-    paint_artifact_compositor_->SetNeedsUpdate();
+    if (paint_artifact_compositor_ &&
+        benchmark_mode ==
+            PaintBenchmarkMode::kForcePaintArtifactCompositorUpdate) {
+      paint_artifact_compositor_->SetNeedsUpdate();
+    }
+    needed_update = !paint_artifact_compositor_ ||
+                    paint_artifact_compositor_->NeedsUpdate();
+    PushPaintArtifactToCompositor(repainted);
   }
 
-  bool needed_update =
-      !paint_artifact_compositor_ || paint_artifact_compositor_->NeedsUpdate();
-  PushPaintArtifactToCompositor(repainted);
   size_t total_animations_count = 0;
   ForAllNonThrottledLocalFrameViews(
       [this, &needed_update,
@@ -2796,22 +2800,6 @@
     }
   }
 
-  // Notify the controller that the artifact has been pushed and some
-  // lifecycle state can be freed (such as raster invalidations).
-  if (paint_controller_)
-    paint_controller_->FinishCycle();
-
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    auto* root = GetLayoutView()->Compositor()->PaintRootGraphicsLayer();
-    if (root) {
-      ForAllPaintingGraphicsLayers(*root, [](GraphicsLayer& layer) {
-        // Notify the paint controller that the artifact has been pushed and
-        // some lifecycle state can be freed (such as raster invalidations).
-        layer.GetPaintController().FinishCycle();
-      });
-    }
-  }
-
   if (paint_artifact_compositor_)
     paint_artifact_compositor_->ClearPropertyTreeChangedState();
 
@@ -2877,7 +2865,8 @@
   });
 }
 
-bool LocalFrameView::PaintTree(PaintBenchmarkMode benchmark_mode) {
+bool LocalFrameView::PaintTree(PaintBenchmarkMode benchmark_mode,
+                               PaintController::CycleScope& cycle_scope) {
   SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
                            LocalFrameUkmAggregator::kPaint);
 
@@ -2925,7 +2914,11 @@
   bool needs_clear_repaint_flags = false;
 
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    // TODO(paint-dev): We should be able to get rid of AddController entirely
+    // after non-CAP code is removed. The call to EnsurePaintController() will
+    // need to be moved up the call stack.
     EnsurePaintController();
+    cycle_scope.AddController(*paint_controller_);
 
     PaintChunkSubset previous_chunks(
         paint_controller_->GetPaintArtifactShared());
@@ -2936,7 +2929,6 @@
     if (paint_controller_->ShouldForcePaintForBenchmark() ||
         GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint() ||
         visual_viewport_or_overlay_needs_repaint_) {
-      paint_controller_->ReserveCapacity();
       GraphicsContext graphics_context(*paint_controller_);
 
       if (Settings* settings = frame_->GetSettings()) {
@@ -2997,6 +2989,8 @@
     // parented into the main frame tree, or when the LocalFrameView is the main
     // frame view of a page overlay. The page overlay is in the layer tree of
     // the host page and will be painted during painting of the host page.
+    // Note that paint_controller_ is not added to cycle_scope, because it is
+    // transient and may be deleted before cycle_scope.
     paint_controller_ =
         std::make_unique<PaintController>(PaintController::kTransient);
     pre_composited_layers_.clear();
@@ -3004,8 +2998,9 @@
 
     if (GraphicsLayer* root =
             layout_view->Compositor()->PaintRootGraphicsLayer()) {
-      repainted = root->PaintRecursively(
-          graphics_context, pre_composited_layers_, benchmark_mode);
+      repainted =
+          root->PaintRecursively(graphics_context, pre_composited_layers_,
+                                 cycle_scope, benchmark_mode);
       if (visual_viewport_or_overlay_needs_repaint_ &&
           paint_artifact_compositor_)
         paint_artifact_compositor_->SetNeedsUpdate();
@@ -4130,16 +4125,15 @@
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     PaintController& paint_controller = EnsurePaintController();
     if (GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint()) {
+      PaintController::CycleScope cycle_scope(paint_controller);
       GraphicsContext graphics_context(paint_controller);
       Paint(graphics_context, kGlobalPaintNormalPhase, cull_rect);
       paint_controller.CommitNewDisplayItems();
     }
-    paint_controller.FinishCycle();
   } else {
     GraphicsLayer* graphics_layer =
         GetLayoutView()->Layer()->GraphicsLayerBacking();
     graphics_layer->PaintForTesting(cull_rect.Rect());
-    graphics_layer->GetPaintController().FinishCycle();
   }
   Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean);
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index cdba46e57..92b108c6 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
 #include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
 #include "third_party/blink/renderer/platform/graphics/subtree_paint_property_update_reason.h"
 #include "third_party/blink/renderer/platform/timer.h"
@@ -862,7 +863,7 @@
   void PerformLayout();
   void PerformPostLayoutTasks(bool view_size_changed);
 
-  bool PaintTree(PaintBenchmarkMode);
+  bool PaintTree(PaintBenchmarkMode, PaintController::CycleScope&);
   void PushPaintArtifactToCompositor(bool repainted);
 
   void ClearLayoutSubtreeRootsAndMarkContainingBlocks();
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.h b/third_party/blink/renderer/core/frame/visual_viewport.h
index b9990c2..ae3e3ec 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.h
+++ b/third_party/blink/renderer/core/frame/visual_viewport.h
@@ -228,9 +228,10 @@
   mojom::blink::ColorScheme UsedColorScheme() const override;
 
   // VisualViewport scrolling may involve pinch zoom and gets routed through
-  // WebViewImpl explicitly rather than via ScrollingCoordinator::DidScroll
-  // since it needs to be set in tandem with the page scale delta.
-  void DidScroll(const FloatPoint&) final { NOTREACHED(); }
+  // WebViewImpl explicitly rather than via
+  // ScrollingCoordinator::DidCompositorScroll() since it needs to be set in
+  // tandem with the page scale delta.
+  void DidCompositorScroll(const FloatPoint&) final { NOTREACHED(); }
 
   // Visual Viewport API implementation.
   double OffsetLeft() const;
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 5ab0e1f..e9c1681a 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -7095,7 +7095,7 @@
   auto* scrollable_area = frame_impl->GetFrameView()->LayoutViewport();
 
   // Do a compositor scroll, verify that this is counted as a user scroll.
-  scrollable_area->DidScroll(FloatPoint(0, 1));
+  scrollable_area->DidCompositorScroll(FloatPoint(0, 1));
   web_view_helper.GetWebView()
       ->MainFrameWidget()
       ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
@@ -7108,7 +7108,7 @@
   initial_scroll_state.was_scrolled_by_user = false;
 
   // The page scale 1.0f and scroll.
-  scrollable_area->DidScroll(FloatPoint(0, 2));
+  scrollable_area->DidCompositorScroll(FloatPoint(0, 2));
   web_view_helper.GetWebView()
       ->MainFrameWidget()
       ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
@@ -7120,7 +7120,7 @@
   initial_scroll_state.was_scrolled_by_user = false;
 
   // No scroll event if there is no scroll delta.
-  scrollable_area->DidScroll(FloatPoint(0, 2));
+  scrollable_area->DidCompositorScroll(FloatPoint(0, 2));
   web_view_helper.GetWebView()
       ->MainFrameWidget()
       ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
@@ -7131,7 +7131,7 @@
   client.Reset();
 
   // Non zero page scale and scroll.
-  scrollable_area->DidScroll(FloatPoint(9, 15));
+  scrollable_area->DidCompositorScroll(FloatPoint(9, 15));
   web_view_helper.GetWebView()
       ->MainFrameWidget()
       ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 098da2b..5c02b66 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -75,8 +75,10 @@
 namespace {
 
 bool HasFormInBetween(const Node* root, const Node* descendant) {
-  DCHECK(descendant->IsDescendantOf(root));
   DCHECK(!IsA<HTMLFormElement>(descendant));
+  // |descendant| might not actually be a descendant of |root|.
+  if (!descendant->IsDescendantOf(root))
+    return false;
   for (ContainerNode* parent = descendant->parentNode();
        parent && parent != root; parent = parent->parentNode()) {
     if (DynamicTo<HTMLFormElement>(parent)) {
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 ba89ae6..7dc0bacc 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -1842,7 +1842,7 @@
   // Do a compositor scroll and set |hover_needs_update_at_scroll_end| to be
   // true in WebViewImpl.
   LocalFrameView* frame_view = GetDocument().View();
-  frame_view->LayoutViewport()->DidScroll(FloatPoint(0, 500));
+  frame_view->LayoutViewport()->DidCompositorScroll(FloatPoint(0, 500));
   WebView().MainFrameWidget()->ApplyViewportChangesForTesting(
       {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false, 0, 0,
        cc::BrowserControlsState::kBoth, true});
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.cc b/third_party/blink/renderer/core/layout/layout_text_control.cc
index b686cad..251f01a 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control.cc
@@ -157,7 +157,6 @@
 // number of Mac fonts, but, in order to get similar rendering across platforms,
 // we do this check for all platforms.
 bool LayoutTextControl::HasValidAvgCharWidth(const Font& font) {
-  const AtomicString family = font.GetFontDescription().Family().Family();
   const SimpleFontData* font_data = font.PrimaryFont();
   DCHECK(font_data);
   if (!font_data)
@@ -172,6 +171,7 @@
   static HashSet<AtomicString>* font_families_with_invalid_char_width_map =
       nullptr;
 
+  const AtomicString& family = font.GetFontDescription().Family().Family();
   if (family.IsEmpty())
     return false;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 899d901..0a4e95e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -471,6 +471,7 @@
   container_builder_.SetInlineSize(inline_size);
   container_builder_.SetMetrics(line_box_metrics);
   container_builder_.SetBfcBlockOffset(block_offset);
+  container_builder_.SetLineBoxBfcBlockOffset(block_offset);
 }
 
 void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item,
@@ -616,6 +617,7 @@
   container_builder_.SetBaseDirection(line_info.BaseDirection());
   if (absl::optional<LayoutUnit> block_offset = result.BfcBlockOffset()) {
     container_builder_.SetBfcBlockOffset(*block_offset);
+    container_builder_.SetLineBoxBfcBlockOffset(*block_offset);
     container_builder_.SetEndMarginStrut(result.EndMarginStrut());
     container_builder_.SetAdjoiningObjectTypes(result.AdjoiningObjectTypes());
     const NGConstraintSpace& space = ConstraintSpace();
@@ -656,8 +658,6 @@
   DCHECK(line_info.IsEmptyLine() || !line_box_metrics.IsEmpty())
       << "Non-empty lines must have a valid set of linebox metrics.";
 
-  bool is_empty_inline = Node().IsEmptyInline();
-
   // All children within the linebox are positioned relative to the baseline,
   // then shifted later using NGLineBoxFragmentBuilder::MoveInBlockDirection.
   LayoutUnit baseline_adjustment =
@@ -711,8 +711,7 @@
       // If we are an empty-inline we may not have the correct BFC block-offset
       // yet. Due to this we need to mark this node as having adjoining
       // objects, and perform a re-layout if our position shifts.
-      if (is_empty_inline)
-        container_builder_.AddAdjoiningObjectTypes(kAdjoiningInlineOutOfFlow);
+      container_builder_.AddAdjoiningObjectTypes(kAdjoiningInlineOutOfFlow);
     } else {
       // A block-level OOF element positions itself on the "next" line. However
       // only shifts down if there is preceding inline-level content.
@@ -1063,7 +1062,7 @@
   // layout_object may be null in certain cases, e.g. if it's a kBidiControl.
   if (layout_object && layout_object->IsBR()) {
     const LayoutUnit line_box_bfc_block_offset =
-        ContainerBfcOffset().block_offset;
+        *container_builder_.LineBoxBfcBlockOffset();
     NGBfcOffset bfc_offset = {LayoutUnit(),
                               line_box_bfc_block_offset + content_size};
     AdjustToClearance(
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index 2da16bc..1b67ab8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -26,6 +26,7 @@
   unpositioned_list_marker_ = NGUnpositionedListMarker();
 
   bfc_block_offset_.reset();
+  line_box_bfc_block_offset_.reset();
 
   size_.inline_size = LayoutUnit();
   metrics_ = FontHeight::Empty();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index 6eded710..e84780e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -59,6 +59,14 @@
   // Mark this line box is an "empty" line box. See NGLineBoxType.
   void SetIsEmptyLineBox();
 
+  absl::optional<LayoutUnit> LineBoxBfcBlockOffset() const {
+    return line_box_bfc_block_offset_;
+  }
+  void SetLineBoxBfcBlockOffset(LayoutUnit offset) {
+    DCHECK(bfc_block_offset_);
+    line_box_bfc_block_offset_ = offset;
+  }
+
   const FontHeight& Metrics() const { return metrics_; }
   void SetMetrics(const FontHeight& metrics) { metrics_ = metrics; }
 
@@ -81,6 +89,7 @@
   scoped_refptr<const NGLayoutResult> ToLineBoxFragment();
 
  private:
+  absl::optional<LayoutUnit> line_box_bfc_block_offset_;
   FontHeight metrics_ = FontHeight::Empty();
   LayoutUnit hang_inline_size_;
   NGPhysicalLineBoxFragment::NGLineBoxType line_box_type_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 8f4b076..0430d266 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -1888,21 +1888,31 @@
   NGFragment fragment(ConstraintSpace().GetWritingDirection(),
                       physical_fragment);
 
-  if (ConstraintSpace().HasBlockFragmentation() &&
-      container_builder_.BfcBlockOffset() && child_bfc_block_offset) {
-    // Floats only cause container separation for the outermost block child that
-    // gets pushed down (the container and the child may have adjoining
-    // block-start margins).
-    bool has_container_separation =
-        has_processed_first_child_ || (layout_result->IsPushedByFloats() &&
-                                       !container_builder_.IsPushedByFloats());
-    NGBreakStatus break_status = BreakBeforeChildIfNeeded(
-        child, *layout_result, previous_inflow_position,
-        *child_bfc_block_offset, has_container_separation);
-    if (break_status == NGBreakStatus::kBrokeBefore)
-      return NGLayoutResult::kSuccess;
-    if (break_status == NGBreakStatus::kNeedsEarlierBreak)
-      return NGLayoutResult::kNeedsEarlierBreak;
+  const absl::optional<LayoutUnit> line_box_bfc_block_offset =
+      layout_result->LineBoxBfcBlockOffset();
+
+  if (ConstraintSpace().HasBlockFragmentation()) {
+    if (container_builder_.BfcBlockOffset() && child_bfc_block_offset) {
+      bool is_line_box_pushed_by_floats =
+          line_box_bfc_block_offset &&
+          *line_box_bfc_block_offset > *child_bfc_block_offset;
+
+      // Floats only cause container separation for the outermost block child
+      // that gets pushed down (the container and the child may have adjoining
+      // block-start margins).
+      bool has_container_separation =
+          has_processed_first_child_ ||
+          (!container_builder_.IsPushedByFloats() &&
+           (layout_result->IsPushedByFloats() || is_line_box_pushed_by_floats));
+      NGBreakStatus break_status = BreakBeforeChildIfNeeded(
+          child, *layout_result, previous_inflow_position,
+          line_box_bfc_block_offset.value_or(*child_bfc_block_offset),
+          has_container_separation);
+      if (break_status == NGBreakStatus::kBrokeBefore)
+        return NGLayoutResult::kSuccess;
+      if (break_status == NGBreakStatus::kNeedsEarlierBreak)
+        return NGLayoutResult::kNeedsEarlierBreak;
+    }
 
     if (inline_child_layout_context) {
       for (auto token : inline_child_layout_context->PropagatedBreakTokens()) {
@@ -1913,6 +1923,9 @@
     }
   }
 
+  if (line_box_bfc_block_offset)
+    child_bfc_block_offset = line_box_bfc_block_offset;
+
   LogicalOffset logical_offset = CalculateLogicalOffset(
       fragment, layout_result->BfcLineOffset(), child_bfc_block_offset);
   if (UNLIKELY(child.IsSliderThumb()))
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index 3121923..0f07c37 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -109,7 +109,14 @@
     scoped_refptr<const NGPhysicalFragment> physical_fragment,
     NGLineBoxFragmentBuilder* builder)
     : NGLayoutResult(std::move(physical_fragment),
-                     static_cast<NGContainerFragmentBuilder*>(builder)) {}
+                     static_cast<NGContainerFragmentBuilder*>(builder)) {
+  DCHECK_EQ(builder->bfc_block_offset_.has_value(),
+            builder->line_box_bfc_block_offset_.has_value());
+  if (builder->bfc_block_offset_ != builder->line_box_bfc_block_offset_) {
+    EnsureRareData()->line_box_bfc_block_offset =
+        builder->line_box_bfc_block_offset_;
+  }
+}
 
 NGLayoutResult::NGLayoutResult(NGContainerFragmentBuilderPassKey key,
                                EStatus status,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index bfda9d6..357d7df 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -164,6 +164,29 @@
     return bfc_offset_.block_offset;
   }
 
+  // The BFC block-offset where a line-box has been placed. Will be nullopt if
+  // it isn't a line-box, or an empty line-box.
+  //
+  // This can be different (but rarely) to where the |BfcBlockOffset()|
+  // resolves to, when floats are present. E.g.
+  //
+  // <div style="width: 100px; display: flow-root;">
+  //   <div style="float: left; width: 200px; height: 20px;"></div>
+  //   text
+  // </div>
+  //
+  // In the above example the |BfcBlockOffset()| will be at 0px, where-as the
+  // |LineBoxBfcBlockOffset()| will be at 20px.
+  absl::optional<LayoutUnit> LineBoxBfcBlockOffset() const {
+    if (!PhysicalFragment().IsLineBox())
+      return absl::nullopt;
+
+    if (HasRareData() && rare_data_->line_box_bfc_block_offset)
+      return rare_data_->line_box_bfc_block_offset;
+
+    return BfcBlockOffset();
+  }
+
   const NGMarginStrut EndMarginStrut() const {
     return HasRareData() ? rare_data_->end_margin_strut : NGMarginStrut();
   }
@@ -427,6 +450,7 @@
               rare_data.tallest_unbreakable_block_size),
           exclusion_space(rare_data.exclusion_space),
           custom_layout_data(rare_data.custom_layout_data),
+          line_box_bfc_block_offset(rare_data.line_box_bfc_block_offset),
           annotation_overflow(rare_data.annotation_overflow),
           block_end_annotation_space(rare_data.block_end_annotation_space),
           has_violating_break(rare_data.has_violating_break),
@@ -463,6 +487,7 @@
     NGExclusionSpace exclusion_space;
     scoped_refptr<SerializedScriptValue> custom_layout_data;
 
+    absl::optional<LayoutUnit> line_box_bfc_block_offset;
     LayoutUnit annotation_overflow;
     LayoutUnit block_end_annotation_space;
     bool has_violating_break = false;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index a1fd7ca..1d60a5d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -1039,7 +1039,7 @@
 
       // Skip over any column spanners.
       if (!fragment || fragment->IsFragmentainerBox()) {
-        const Vector<NodeToLayout>& pending_descendants =
+        Vector<NodeToLayout>& pending_descendants =
             descendants_to_layout[index];
         LayoutOOFsInFragmentainer(pending_descendants, index,
                                   column_inline_progression,
@@ -1144,11 +1144,11 @@
 }
 
 scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutOOFNode(
-    const NodeToLayout& oof_node_to_layout,
+    NodeToLayout& oof_node_to_layout,
     const LayoutBox* only_layout,
     const NGConstraintSpace* fragmentainer_constraint_space) {
   const NodeInfo& node_info = oof_node_to_layout.node_info;
-  OffsetInfo offset_info = oof_node_to_layout.offset_info;
+  OffsetInfo& offset_info = oof_node_to_layout.offset_info;
   if (offset_info.has_cached_layout_result) {
     DCHECK(offset_info.initial_layout_result);
     return offset_info.initial_layout_result;
@@ -1158,7 +1158,7 @@
       freeze_scrollbars;
   do {
     scoped_refptr<const NGLayoutResult> layout_result =
-        Layout(oof_node_to_layout, offset_info, fragmentainer_constraint_space);
+        Layout(oof_node_to_layout, fragmentainer_constraint_space);
 
     if (!freeze_scrollbars.has_value()) {
       // Since out-of-flow positioning sets up a constraint space with fixed
@@ -1283,7 +1283,6 @@
 
 scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
     const NodeToLayout& oof_node_to_layout,
-    const OffsetInfo& offset_info,
     const NGConstraintSpace* fragmentainer_constraint_space) {
   const NodeInfo& node_info = oof_node_to_layout.node_info;
   const WritingDirectionMode candidate_writing_direction =
@@ -1291,6 +1290,7 @@
   LogicalSize container_content_size_in_candidate_writing_mode =
       node_info.container_physical_content_size.ConvertToLogical(
           candidate_writing_direction.GetWritingMode());
+  const OffsetInfo& offset_info = oof_node_to_layout.offset_info;
   LogicalOffset offset = offset_info.offset;
 
   // Reset the |layout_result| computed earlier to allow fragmentation in the
@@ -1404,7 +1404,7 @@
 }
 
 void NGOutOfFlowLayoutPart::LayoutOOFsInFragmentainer(
-    const Vector<NodeToLayout>& pending_descendants,
+    Vector<NodeToLayout>& pending_descendants,
     wtf_size_t index,
     LayoutUnit column_inline_progression,
     Vector<NodeToLayout>* fragmented_descendants) {
@@ -1493,7 +1493,7 @@
 }
 
 void NGOutOfFlowLayoutPart::AddOOFToFragmentainer(
-    const NodeToLayout& descendant,
+    NodeToLayout& descendant,
     const NGConstraintSpace* fragmentainer_space,
     LogicalOffset fragmentainer_offset,
     wtf_size_t index,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 66b9ec78..c3b4d0f2 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -226,7 +226,7 @@
   NodeInfo SetupNodeInfo(const NGLogicalOutOfFlowPositionedNode& oof_node);
 
   scoped_refptr<const NGLayoutResult> LayoutOOFNode(
-      const NodeToLayout& oof_node_to_layout,
+      NodeToLayout& oof_node_to_layout,
       const LayoutBox* only_layout,
       const NGConstraintSpace* fragmentainer_constraint_space = nullptr);
 
@@ -238,7 +238,6 @@
 
   scoped_refptr<const NGLayoutResult> Layout(
       const NodeToLayout& oof_node_to_layout,
-      const OffsetInfo& offset_info,
       const NGConstraintSpace* fragmentainer_constraint_space);
 
   bool IsContainingBlockForCandidate(const NGLogicalOutOfFlowPositionedNode&);
@@ -262,12 +261,11 @@
   // |fragmented_descendants| is also an output variable in that any OOF that
   // has not finished layout in the current pass will be added back to
   // |fragmented_descendants| to continue layout in the next fragmentainer.
-  void LayoutOOFsInFragmentainer(
-      const Vector<NodeToLayout>& pending_descendants,
-      wtf_size_t index,
-      LayoutUnit column_inline_progression,
-      Vector<NodeToLayout>* fragmented_descendants);
-  void AddOOFToFragmentainer(const NodeToLayout& descendant,
+  void LayoutOOFsInFragmentainer(Vector<NodeToLayout>& pending_descendants,
+                                 wtf_size_t index,
+                                 LayoutUnit column_inline_progression,
+                                 Vector<NodeToLayout>* fragmented_descendants);
+  void AddOOFToFragmentainer(NodeToLayout& descendant,
                              const NGConstraintSpace* fragmentainer_space,
                              LogicalOffset fragmentainer_offset,
                              wtf_size_t index,
diff --git a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
new file mode 100644
index 0000000..ceacd700
--- /dev/null
+++ b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
@@ -0,0 +1,192 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this sink code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h"
+#include "third_party/blink/renderer/core/loader/resource/script_resource.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/testing/code_cache_loader_mock.h"
+#include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h"
+#include "third_party/blink/renderer/platform/testing/noop_web_url_loader.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+
+namespace blink {
+namespace {
+
+class ResourceLoaderCodeCacheTest : public testing::Test {
+ protected:
+  static scoped_refptr<base::SingleThreadTaskRunner> CreateTaskRunner() {
+    return base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  }
+
+  ResourceFetcher* MakeResourceFetcher(
+      TestResourceFetcherProperties* properties,
+      FetchContext* context,
+      ResourceFetcher::LoaderFactory* loader_factory) {
+    return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        properties->MakeDetachable(), context, CreateTaskRunner(),
+        CreateTaskRunner(), loader_factory,
+        MakeGarbageCollected<MockContextLifecycleNotifier>(),
+        nullptr /* back_forward_cache_loader_helper */));
+  }
+
+  class CodeCacheTestLoaderFactory : public ResourceFetcher::LoaderFactory {
+   public:
+    explicit CodeCacheTestLoaderFactory(
+        scoped_refptr<CodeCacheLoaderMock::Controller> controller)
+        : controller_(std::move(controller)) {}
+    std::unique_ptr<WebURLLoader> CreateURLLoader(
+        const ResourceRequest& request,
+        const ResourceLoaderOptions& options,
+        scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner,
+        scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner,
+        WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper)
+        override {
+      return std::make_unique<NoopWebURLLoader>(
+          std::move(freezable_task_runner));
+    }
+    std::unique_ptr<WebCodeCacheLoader> CreateCodeCacheLoader() override {
+      return std::make_unique<CodeCacheLoaderMock>(controller_);
+    }
+
+   private:
+    scoped_refptr<CodeCacheLoaderMock::Controller> controller_;
+  };
+
+  void CommonSetup(const char* url_string = nullptr) {
+    SchemeRegistry::RegisterURLSchemeAsCodeCacheWithHashing(
+        "codecachewithhashing");
+
+    auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
+    FetchContext* context = MakeGarbageCollected<MockFetchContext>();
+    controller_ = base::MakeRefCounted<CodeCacheLoaderMock::Controller>();
+    controller_->DelayResponse();
+    auto* loader_factory =
+        MakeGarbageCollected<CodeCacheTestLoaderFactory>(controller_);
+    auto* fetcher = MakeResourceFetcher(properties, context, loader_factory);
+
+    KURL url(url_string ? url_string
+                        : "codecachewithhashing://www.example.com/");
+    ResourceRequest request(url);
+    request.SetRequestContext(mojom::blink::RequestContextType::SCRIPT);
+
+    FetchParameters params = FetchParameters::CreateForTest(std::move(request));
+    resource_ = ScriptResource::Fetch(params, fetcher, nullptr,
+                                      ScriptResource::kNoStreaming);
+    loader_ = resource_->Loader();
+
+    response_ = ResourceResponse(url);
+    response_.SetHttpStatusCode(200);
+  }
+
+  std::vector<uint8_t> MakeSerializedCodeCacheData() {
+    const size_t kCachedMetadataTypeSize = sizeof(uint32_t);
+    const size_t kSha256Bytes = 256 / 8;
+    const size_t kDataSize =
+        kCachedMetadataTypeSize + kSha256Bytes + kCachedMetaDataStart + 1;
+    std::vector<uint8_t> data(kDataSize);
+    *reinterpret_cast<uint32_t*>(&data[0]) =
+        CachedMetadataHandler::kSingleEntryWithHash;
+    *reinterpret_cast<uint32_t*>(
+        &data[kCachedMetadataTypeSize + kSha256Bytes]) =
+        CachedMetadataHandler::kSingleEntry;
+    return data;
+  }
+
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      platform_;
+
+  // State initialized by CommonSetup().
+  Persistent<ScriptResource> resource_;
+  Persistent<ResourceLoader> loader_;
+  ResourceResponse response_;
+  scoped_refptr<CodeCacheLoaderMock::Controller> controller_;
+};
+
+TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheEmptyResponseFirst) {
+  CommonSetup();
+
+  loader_->DidReceiveResponse(WrappedResourceResponse(response_));
+
+  // Nothing has changed yet because the code cache hasn't yet responded.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+
+  // An empty code cache response means no data was found.
+  controller_->Respond(base::Time(), mojo_base::BigBuffer());
+
+  // No code cache data was present.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+}
+
+TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheEmptyResponseSecond) {
+  CommonSetup();
+
+  // An empty code cache response means no data was found.
+  controller_->Respond(base::Time(), mojo_base::BigBuffer());
+
+  // Nothing has changed yet because the content response hasn't arrived yet.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+
+  loader_->DidReceiveResponse(WrappedResourceResponse(response_));
+
+  // No code cache data was present.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+}
+
+TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheFullResponseFirst) {
+  CommonSetup();
+
+  loader_->DidReceiveResponse(WrappedResourceResponse(response_));
+
+  // Nothing has changed yet because the code cache hasn't yet responded.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+
+  controller_->Respond(base::Time(),
+                       mojo_base::BigBuffer(MakeSerializedCodeCacheData()));
+
+  // Code cache data was present.
+  EXPECT_TRUE(resource_->CodeCacheSize());
+}
+
+TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheFullResponseSecond) {
+  CommonSetup();
+
+  controller_->Respond(base::Time(),
+                       mojo_base::BigBuffer(MakeSerializedCodeCacheData()));
+
+  // Nothing has changed yet because the content response hasn't arrived yet.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+
+  loader_->DidReceiveResponse(WrappedResourceResponse(response_));
+
+  // Code cache data was present.
+  EXPECT_TRUE(resource_->CodeCacheSize());
+}
+
+TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheFullHttpsScheme) {
+  CommonSetup("https://www.example.com/");
+
+  controller_->Respond(base::Time(),
+                       mojo_base::BigBuffer(MakeSerializedCodeCacheData()));
+
+  // Nothing has changed yet because the content response hasn't arrived yet.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+
+  loader_->DidReceiveResponse(WrappedResourceResponse(response_));
+
+  // Since the URL was https, and the response times were not set, the cached
+  // metadata should not be set.
+  EXPECT_FALSE(resource_->CodeCacheSize());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.cc b/third_party/blink/renderer/core/loader/resource/script_resource.cc
index 2087037..22d6955 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/script_resource.cc
@@ -203,7 +203,12 @@
 
 bool ScriptResource::CodeCacheHashRequired() const {
   if (cached_metadata_handler_) {
-    return cached_metadata_handler_->HashRequired();
+    bool result = cached_metadata_handler_->HashRequired();
+    if (result) {
+      DCHECK(SchemeRegistry::SchemeSupportsCodeCacheWithHashing(
+          GetResourceRequest().Url().Protocol()));
+    }
+    return result;
   }
   return false;
 }
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 3103222d8..15c6c3b 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -2849,7 +2849,7 @@
                          .GlobalRootScroller();
     ScrollableArea* scrollable_area =
         To<LayoutBox>(scroller->GetLayoutObject())->GetScrollableArea();
-    scrollable_area->DidScroll(FloatPoint(0, 100000));
+    scrollable_area->DidCompositorScroll(FloatPoint(0, 100000));
 
     WebView().ResizeWithBrowserControls(gfx::Size(400, 450), 50, 50, false);
 
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
index f49b412..a466b7a 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
@@ -90,20 +90,21 @@
   return nullptr;
 }
 
-void ScrollingCoordinator::DidScroll(
+void ScrollingCoordinator::DidCompositorScroll(
     CompositorElementId element_id,
     const gfx::ScrollOffset& offset,
     const absl::optional<cc::TargetSnapAreaElementIds>& snap_target_ids) {
   // Find the associated scrollable area using the element id and notify it of
   // the compositor-side scroll. We explicitly do not check the VisualViewport
-  // which handles scroll offset differently (see: VisualViewport::didScroll).
-  // Remote frames will receive DidScroll callbacks from their own compositor.
+  // which handles scroll offset differently (see:
+  // VisualViewport::DidCompositorScroll). Remote frames will receive
+  // DidCompositorScroll callbacks from their own compositor.
   // The ScrollableArea with matching ElementId may have been deleted and we can
-  // safely ignore the DidScroll callback.
+  // safely ignore the DidCompositorScroll callback.
   auto* scrollable = ScrollableAreaWithElementIdInAllLocalFrames(element_id);
   if (!scrollable)
     return;
-  scrollable->DidScroll(FloatPoint(offset.x(), offset.y()));
+  scrollable->DidCompositorScroll(FloatPoint(offset.x(), offset.y()));
   if (snap_target_ids)
     scrollable->SetTargetSnapAreaElementIds(snap_target_ids.value());
 }
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
index a4ccbf16..a8fb4454 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
@@ -113,9 +113,10 @@
       const CompositorElementId&);
 
   // ScrollCallbacks implementation
-  void DidScroll(CompositorElementId,
-                 const gfx::ScrollOffset&,
-                 const absl::optional<cc::TargetSnapAreaElementIds>&) override;
+  void DidCompositorScroll(
+      CompositorElementId,
+      const gfx::ScrollOffset&,
+      const absl::optional<cc::TargetSnapAreaElementIds>&) override;
   void DidChangeScrollbarsHidden(CompositorElementId, bool hidden) override;
 
   base::WeakPtr<ScrollingCoordinator> GetWeakPtr() {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index a673dfe..072c1af 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -181,8 +181,8 @@
   return box ? box->GetScrollableArea() : nullptr;
 }
 
-void PaintLayerScrollableArea::DidScroll(const FloatPoint& position) {
-  ScrollableArea::DidScroll(position);
+void PaintLayerScrollableArea::DidCompositorScroll(const FloatPoint& position) {
+  ScrollableArea::DidCompositorScroll(position);
   // This should be alive if it receives composited scroll callbacks.
   CHECK(!HasBeenDisposed());
 }
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 99148b6..d794536 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -278,7 +278,7 @@
   // only a helper.
   cc::Layer* LayerForScrolling() const override;
 
-  void DidScroll(const FloatPoint&) override;
+  void DidCompositorScroll(const FloatPoint&) override;
 
   // GraphicsLayers for the scrolling components.
   // Any function can return nullptr if they are not accelerated.
diff --git a/third_party/blink/renderer/core/script/dynamic_module_resolver.cc b/third_party/blink/renderer/core/script/dynamic_module_resolver.cc
index 2089cb1..0986094 100644
--- a/third_party/blink/renderer/core/script/dynamic_module_resolver.cc
+++ b/third_party/blink/renderer/core/script/dynamic_module_resolver.cc
@@ -266,17 +266,28 @@
       << "ResolveDynamically should be called from V8 callback, within a valid "
          "context.";
 
-  // <spec step="4.1">Let referencing script be
-  // referencingScriptOrModule.[[HostDefined]].</spec>
+  // <spec step="5">If referencingScriptOrModule is not null, then:</spec>
+  KURL base_url;
+  if (referrer_info.HasReferencingScript()) {
+    // <spec step="5.3">Set base URL to referencing script's base URL.</spec>
+    base_url = referrer_info.BaseURL();
 
-  // <spec step="4.3">Set base URL to referencing script's base URL.</spec>
-  KURL base_url = referrer_info.BaseURL();
-  if (base_url.IsNull()) {
+    // Fallback to ExecutionContext's Base URL. This only occurs for
+    // setTimeout()/setInterval() and some internal scripts not specified by the
+    // HTML spec (i.e. some of `ClassicScript::CreateUnspecifiedScript()`),
+    // where `ReferrerScriptInfo::BaseURL()` is null.
+    // TODO(crbug.com/1133238): Perhaps remove this fallback or change to
+    // about:blank after base URLs are assigned to setTimeout() etc.
+    if (base_url.IsNull()) {
+      base_url =
+          ExecutionContext::From(modulator_->GetScriptState())->BaseURL();
+    }
+  } else {
     // The case where "referencing script" doesn't exist.
     //
     // <spec step="1">Let settings object be the current settings object.</spec>
     //
-    // <spec step="2">Let base URL be settings object's API base URL.</spec>
+    // <spec step="3">Let base URL be settings object's API base URL.</spec>
     base_url = ExecutionContext::From(modulator_->GetScriptState())->BaseURL();
   }
   DCHECK(!base_url.IsNull());
@@ -334,21 +345,25 @@
     return;
   }
 
-  // <spec step="4.4">Set fetch options to the descendant script fetch options
+  // <spec step="4">Let fetch options be the default classic script fetch
+  // options.</spec>
+  //
+  // <spec step="5.4">Set fetch options to the descendant script fetch options
   // for referencing script's fetch options.</spec>
   //
   // <spec
-  // href="https://html.spec.whatwg.org/C/#descendant-script-fetch-options"> For
-  // any given script fetch options options, the descendant script fetch options
-  // are a new script fetch options whose items all have the same values, except
-  // for the integrity metadata, which is instead the empty string.</spec>
+  // href="https://html.spec.whatwg.org/C/#descendant-script-fetch-options">
+  // For any given script fetch options options, the descendant script fetch
+  // options are a new script fetch options whose items all have the same
+  // values, except for the integrity metadata, which is instead the empty
+  // string.</spec>
   //
-  // TODO(domfarolino): It has not yet been decided how a script's "importance"
-  // should affect its dynamic imports. There is discussion at
-  // https://github.com/whatwg/html/issues/3670, but for now there is no effect,
-  // and dynamic imports get kImportanceAuto. If this changes,
-  // ReferrerScriptInfo will need a mojom::FetchImportanceMode member, that must
-  // be properly set.
+  // TODO(domfarolino): It has not yet been decided how a script's
+  // "importance" should affect its dynamic imports. There is discussion at
+  // https://github.com/whatwg/html/issues/3670, but for now there is no
+  // effect, and dynamic imports get kImportanceAuto. If this changes,
+  // ReferrerScriptInfo will need a mojom::FetchImportanceMode member, that
+  // must be properly set.
   ScriptFetchOptions options(referrer_info.Nonce(), IntegrityMetadataSet(),
                              String(), referrer_info.ParserState(),
                              referrer_info.CredentialsMode(),
diff --git a/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc b/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc
index ad590e0..be6ce29 100644
--- a/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc
+++ b/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc
@@ -40,7 +40,8 @@
   return KURL(kTestDependencyURLJSON);
 }
 ReferrerScriptInfo TestReferrerScriptInfo() {
-  return ReferrerScriptInfo(TestReferrerURL(), ScriptFetchOptions());
+  return ReferrerScriptInfo::CreateWithReferencingScript(TestReferrerURL(),
+                                                         ScriptFetchOptions());
 }
 
 class DynamicModuleResolverTestModulator final : public DummyModulator {
@@ -445,7 +446,8 @@
   ModuleRequest module_request("./dependency.js",
                                TextPosition::MinimumPosition(),
                                Vector<ImportAssertion>());
-  resolver->ResolveDynamically(module_request, ReferrerScriptInfo(),
+  resolver->ResolveDynamically(module_request,
+                               ReferrerScriptInfo::CreateNoReferencingScript(),
                                promise_resolver);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
@@ -482,10 +484,10 @@
   ModuleRequest module_request("./dependency.js",
                                TextPosition::MinimumPosition(),
                                Vector<ImportAssertion>());
-  resolver->ResolveDynamically(
-      module_request,
-      ReferrerScriptInfo(correct_base_url, ScriptFetchOptions()),
-      promise_resolver);
+  resolver->ResolveDynamically(module_request,
+                               ReferrerScriptInfo::CreateWithReferencingScript(
+                                   correct_base_url, ScriptFetchOptions()),
+                               promise_resolver);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
   EXPECT_TRUE(modulator->fetch_tree_was_called());
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index e66e82a..7abea57 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -898,7 +898,7 @@
                  std::max(0, size.Height() - HorizontalScrollbarHeight()));
 }
 
-void ScrollableArea::DidScroll(const FloatPoint& position) {
+void ScrollableArea::DidCompositorScroll(const FloatPoint& position) {
   ScrollOffset new_offset(ScrollPositionToOffset(position));
   SetScrollOffset(new_offset, mojom::blink::ScrollType::kCompositor);
 }
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index ee4f50a..d17a578 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -526,7 +526,7 @@
       const = 0;
 
   // Callback for compositor-side scrolling.
-  virtual void DidScroll(const FloatPoint&);
+  virtual void DidCompositorScroll(const FloatPoint& position);
 
   virtual void ScrollbarFrameRectChanged() {}
 
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area_test.cc b/third_party/blink/renderer/core/scroll/scrollable_area_test.cc
index 8cfee51..7d06943 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area_test.cc
@@ -285,7 +285,7 @@
 
   MockScrollableArea* scrollable_area =
       MockScrollableArea::Create(ScrollOffset(100, 100));
-  scrollable_area->DidScroll(FloatPoint(40, 51));
+  scrollable_area->DidCompositorScroll(FloatPoint(40, 51));
 
   EXPECT_EQ(40, scrollable_area->ScrollOffsetInt().Width());
   EXPECT_EQ(51, scrollable_area->ScrollOffsetInt().Height());
diff --git a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
index c84d33e..c042969 100644
--- a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
+++ b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
@@ -196,6 +196,7 @@
 }
 
 void SVGFilterBuilder::Add(const AtomicString& id, FilterEffect* effect) {
+  DCHECK(effect);
   if (id.IsEmpty()) {
     last_effect_ = effect;
     return;
@@ -210,20 +211,21 @@
 
 FilterEffect* SVGFilterBuilder::GetEffectById(const AtomicString& id) const {
   if (!id.IsEmpty()) {
-    if (FilterEffect* builtin_effect =
-            builtin_effects_.DeprecatedAtOrEmptyValue(id))
-      return builtin_effect;
+    auto builtin_it = builtin_effects_.find(id);
+    if (builtin_it != builtin_effects_.end())
+      return builtin_it->value;
 
-    if (FilterEffect* named_effect =
-            named_effects_.DeprecatedAtOrEmptyValue(id))
-      return named_effect;
+    auto named_it = named_effects_.find(id);
+    if (named_it != named_effects_.end())
+      return named_it->value;
   }
 
   if (last_effect_)
     return last_effect_;
 
-  return builtin_effects_.DeprecatedAtOrEmptyValue(
-      FilterInputKeywords::GetSourceGraphic());
+  // Fallback to the 'SourceGraphic' input. We add it in the constructor so it will always be
+  // present.
+  return builtin_effects_.at(FilterInputKeywords::GetSourceGraphic());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 5f3f9f31..93d12ad8 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -557,6 +557,7 @@
     return nullptr;
 
   view->UpdateAllLifecyclePhasesExceptPaint(DocumentUpdateReason::kSVGImage);
+  PaintController::CycleScope cycle_scope(*paint_controller_);
   PaintRecordBuilder builder(*paint_controller_);
   builder.Context().SetDarkModeEnabled(draw_info.IsDarkModeEnabled());
   view->PaintOutsideOfLifecycle(builder.Context(), kGlobalPaintNormalPhase);
diff --git a/third_party/blink/renderer/core/testing/mock_clipboard_host.cc b/third_party/blink/renderer/core/testing/mock_clipboard_host.cc
index e0ff1f0..a90377e 100644
--- a/third_party/blink/renderer/core/testing/mock_clipboard_host.cc
+++ b/third_party/blink/renderer/core/testing/mock_clipboard_host.cc
@@ -184,6 +184,37 @@
   needs_reset_ = true;
 }
 
+void MockClipboardHost::ReadAvailableCustomAndStandardFormats(
+    ReadAvailableCustomAndStandardFormatsCallback callback) {
+  Vector<String> format_names;
+  for (const auto& item : unsanitized_custom_data_map_)
+    format_names.emplace_back(item.key);
+  std::move(callback).Run(format_names);
+}
+
+void MockClipboardHost::ReadUnsanitizedCustomFormat(
+    const String& format,
+    ReadUnsanitizedCustomFormatCallback callback) {
+  const auto it = unsanitized_custom_data_map_.find(format);
+  if (it == unsanitized_custom_data_map_.end())
+    return;
+
+  mojo_base::BigBuffer buffer =
+      mojo_base::BigBuffer(base::make_span(it->value.data(), it->value.size()));
+  std::move(callback).Run(std::move(buffer));
+}
+
+void MockClipboardHost::WriteUnsanitizedCustomFormat(
+    const String& format,
+    mojo_base::BigBuffer data) {
+  if (needs_reset_)
+    Reset();
+  // Simulate the underlying platform copying this data.
+  Vector<uint8_t> data_copy(base::saturated_cast<wtf_size_t>(data.size()),
+                            *data.data());
+  unsanitized_custom_data_map_.Set(format, data_copy);
+}
+
 #if defined(OS_MAC)
 void MockClipboardHost::WriteStringToFindPboard(const String& text) {}
 #endif
diff --git a/third_party/blink/renderer/core/testing/mock_clipboard_host.h b/third_party/blink/renderer/core/testing/mock_clipboard_host.h
index 8c40b95e..8957dbae 100644
--- a/third_party/blink/renderer/core/testing/mock_clipboard_host.h
+++ b/third_party/blink/renderer/core/testing/mock_clipboard_host.h
@@ -59,6 +59,13 @@
   void WriteBookmark(const String& url, const String& title) override;
   void WriteImage(const SkBitmap& bitmap) override;
   void CommitWrite() override;
+  void ReadAvailableCustomAndStandardFormats(
+      ReadAvailableCustomAndStandardFormatsCallback callback) override;
+  void ReadUnsanitizedCustomFormat(
+      const String& format,
+      ReadUnsanitizedCustomFormatCallback callback) override;
+  void WriteUnsanitizedCustomFormat(const String& format,
+                                    mojo_base::BigBuffer data) override;
 #if defined(OS_MAC)
   void WriteStringToFindPboard(const String& text) override;
 #endif
@@ -75,6 +82,7 @@
   HashMap<String, String> custom_data_;
   bool write_smart_paste_ = false;
   bool needs_reset_ = false;
+  HashMap<String, Vector<uint8_t>> unsanitized_custom_data_map_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_user_timing.cc b/third_party/blink/renderer/core/timing/performance_user_timing.cc
index a0ff985a..887e3330 100644
--- a/third_party/blink/renderer/core/timing/performance_user_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_user_timing.cc
@@ -109,7 +109,8 @@
   }
 
   PerformanceTiming::PerformanceTimingGetter timing_function =
-      PerformanceTiming::GetAttributeMapping().at(mark_name);
+      PerformanceTiming::GetAttributeMapping().DeprecatedAtOrEmptyValue(
+          mark_name);
   if (!timing_function) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kSyntaxError,
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
index 28e4d473..7fb38eafa 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
@@ -261,6 +261,9 @@
 // static
 bool ClipboardWriter::IsValidType(const String& type,
                                   bool is_custom_format_type) {
+  if (is_custom_format_type)
+    return type.length() < mojom::blink::ClipboardHost::kMaxFormatSize;
+
   if (type == kMimeTypeImageSvg)
     return RuntimeEnabledFeatures::ClipboardSvgEnabled();
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
index e207fc4b..7f6a296 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
@@ -36,6 +36,7 @@
 namespace {
 
 const int kThumbRadius = 6;
+const base::TimeDelta kRenderTimelineInterval = base::TimeDelta::FromSeconds(1);
 
 // Only respond to main button of primary pointer(s).
 bool IsValidPointerEvent(const blink::Event& event) {
@@ -82,6 +83,13 @@
 
 void MediaControlTimelineElement::SetPosition(double current_time,
                                               bool suppress_aria) {
+  if (is_live_ && !live_anchor_time_ && current_time != 0) {
+    live_anchor_time_.emplace();
+    live_anchor_time_->clock_time_ = base::TimeTicks::Now();
+    live_anchor_time_->media_time_ = MediaElement().currentTime();
+  }
+
+  MaybeUpdateTimelineInterval();
   setValue(String::Number(current_time));
 
   if (!suppress_aria)
@@ -91,8 +99,11 @@
 }
 
 void MediaControlTimelineElement::SetDuration(double duration) {
-  double duration_value = std::isfinite(duration) ? duration : 0;
-  SetFloatingPointAttribute(html_names::kMaxAttr, duration_value);
+  is_live_ = std::isinf(duration);
+  double duration_value = duration;
+  SetFloatingPointAttribute(html_names::kMaxAttr,
+                            is_live_ ? 0.0 : duration_value);
+  SetFloatingPointAttribute(html_names::kMinAttr, 0.0);
   RenderBarSegments();
 }
 
@@ -109,10 +120,12 @@
   if (BeginScrubbingEvent(event)) {
     Platform::Current()->RecordAction(
         UserMetricsAction("Media.Controls.ScrubbingBegin"));
+    is_scrubbing_ = true;
     GetMediaControls().BeginScrubbing(MediaControlsImpl::IsTouchEvent(&event));
   } else if (EndScrubbingEvent(event)) {
     Platform::Current()->RecordAction(
         UserMetricsAction("Media.Controls.ScrubbingEnd"));
+    is_scrubbing_ = false;
     GetMediaControls().EndScrubbing();
   }
 
@@ -163,6 +176,58 @@
       event, GetLayoutObject());
 }
 
+void MediaControlTimelineElement::OnMediaPlaying() {
+  if (!is_live_)
+    return;
+
+  if (render_timeline_timer_.IsRunning())
+    render_timeline_timer_.Stop();
+}
+
+void MediaControlTimelineElement::OnMediaStoppedPlaying() {
+  if (!is_live_ || is_scrubbing_ || !live_anchor_time_)
+    return;
+
+  render_timeline_timer_.Start(
+      FROM_HERE, kRenderTimelineInterval,
+      WTF::BindRepeating(&MediaControlTimelineElement::UpdateLiveTimeline,
+                         WrapWeakPersistent(this)));
+}
+
+void MediaControlTimelineElement::OnProgress() {
+  MaybeUpdateTimelineInterval();
+  RenderBarSegments();
+}
+
+void MediaControlTimelineElement::UpdateLiveTimeline() {
+  MaybeUpdateTimelineInterval();
+  RenderBarSegments();
+}
+
+void MediaControlTimelineElement::MaybeUpdateTimelineInterval() {
+  if (!is_live_ || !MediaElement().seekable()->length() || !live_anchor_time_)
+    return;
+
+  int last_seekable = MediaElement().seekable()->length() - 1;
+  double seekable_start =
+      MediaElement().seekable()->start(last_seekable, ASSERT_NO_EXCEPTION);
+  double seekable_end =
+      MediaElement().seekable()->end(last_seekable, ASSERT_NO_EXCEPTION);
+  double expected_media_time_now =
+      live_anchor_time_->media_time_ +
+      (base::TimeTicks::Now() - live_anchor_time_->clock_time_).InSecondsF();
+
+  // Cap the current live time in seekable range.
+  if (expected_media_time_now > seekable_end) {
+    live_anchor_time_->media_time_ = seekable_end;
+    live_anchor_time_->clock_time_ = base::TimeTicks::Now();
+    expected_media_time_now = seekable_end;
+  }
+
+  SetFloatingPointAttribute(html_names::kMinAttr, seekable_start);
+  SetFloatingPointAttribute(html_names::kMaxAttr, expected_media_time_now);
+}
+
 void MediaControlTimelineElement::RenderBarSegments() {
   SetupBarSegments();
 
@@ -174,6 +239,16 @@
   // buffered range containing the current play head.
   TimeRanges* buffered_time_ranges = MediaElement().buffered();
   DCHECK(buffered_time_ranges);
+
+  // Calculate |current_time| and |duration| for live media base on the timeline
+  // value since timeline's minimum value is not necessarily zero.
+  if (is_live_) {
+    current_time =
+        value().ToDouble() - GetFloatingPointAttribute(html_names::kMinAttr);
+    duration = GetFloatingPointAttribute(html_names::kMaxAttr) -
+               GetFloatingPointAttribute(html_names::kMinAttr);
+  }
+
   if (std::isnan(duration) || std::isinf(duration) || !duration ||
       std::isnan(current_time)) {
     SetBeforeSegmentPosition(MediaControlSliderElement::Position(0, 0));
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.h
index c40ec8d..f0c62f0 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_TIMELINE_ELEMENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_TIMELINE_ELEMENT_H_
 
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_slider_element.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 
@@ -28,6 +31,10 @@
 
   void OnMediaKeyboardEvent(Event* event) { DefaultEventHandler(*event); }
 
+  void OnMediaPlaying();
+  void OnMediaStoppedPlaying();
+  void OnProgress();
+
   void RenderBarSegments();
 
   // Inform the timeline that the Media Controls have been shown or hidden.
@@ -40,9 +47,18 @@
   const char* GetNameForHistograms() const override;
 
  private:
+  // Struct used to track the current live time.
+  struct LiveAnchorTime {
+    base::TimeTicks clock_time_;
+    double media_time_ = 0;
+  };
+
   void DefaultEventHandler(Event&) override;
   bool KeepEventInNode(const Event&) const override;
 
+  void UpdateLiveTimeline();
+  void MaybeUpdateTimelineInterval();
+
   // Checks if we can begin or end a scrubbing event. If the event is a pointer
   // event then it needs to start and end with valid pointer events. If the
   // event is a pointer event followed by a touch event then it can only be
@@ -55,6 +71,14 @@
   bool is_touching_ = false;
 
   bool controls_hidden_ = false;
+
+  bool is_scrubbing_ = false;
+
+  bool is_live_ = false;
+
+  absl::optional<LiveAnchorTime> live_anchor_time_;
+
+  base::RepeatingTimer render_timeline_timer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
index a260d500..1b17f93 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -1864,11 +1864,13 @@
 void MediaControlsImpl::OnPlaying() {
   StartHideMediaControlsTimer();
   UpdateCSSClassFromState();
+  timeline_->OnMediaPlaying();
 }
 
 void MediaControlsImpl::OnPause() {
   UpdatePlayState();
   UpdateTimeIndicators();
+  timeline_->OnMediaStoppedPlaying();
   MakeOpaque();
 
   StopHideMediaControlsTimer();
@@ -1993,7 +1995,7 @@
 }
 
 void MediaControlsImpl::OnLoadingProgress() {
-  timeline_->RenderBarSegments();
+  timeline_->OnProgress();
 }
 
 void MediaControlsImpl::ComputeWhichControlsFit() {
@@ -2193,6 +2195,7 @@
 }
 
 void MediaControlsImpl::OnWaiting() {
+  timeline_->OnMediaStoppedPlaying();
   UpdateCSSClassFromState();
 }
 
diff --git a/third_party/blink/renderer/modules/mediasource/DEPS b/third_party/blink/renderer/modules/mediasource/DEPS
index a7b4b9d..27f0397 100644
--- a/third_party/blink/renderer/modules/mediasource/DEPS
+++ b/third_party/blink/renderer/modules/mediasource/DEPS
@@ -1,7 +1,9 @@
 include_rules = [
     "-third_party/blink/renderer/modules",
+    "+base/command_line.h",
     "+media/base/audio_decoder_config.h",
     "+media/base/logging_override_if_enabled.h",
+    "+media/base/media_switches.h",
     "+media/base/mime_util.h",
     "+media/base/stream_parser.h",
     "+media/base/stream_parser_buffer.h",
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.cc b/third_party/blink/renderer/modules/mediasource/media_source.cc
index 537a31ce..85679fec 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.cc
+++ b/third_party/blink/renderer/modules/mediasource/media_source.cc
@@ -6,10 +6,12 @@
 
 #include <memory>
 
+#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/logging_override_if_enabled.h"
+#include "media/base/media_switches.h"
 #include "media/base/mime_util.h"
 #include "media/base/supported_types.h"
 #include "media/base/video_decoder_config.h"
@@ -553,7 +555,13 @@
   // |enforce_codec_specificity| to understand if we are servicing iTS (if true)
   // versus aSB (if false). If servicing aSB or cT, we'll remove any detected
   // hevc codec from the codecs we use in the GetSupportsType() query.
-  if (!enforce_codec_specificity) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  const bool allow_hevc = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kLacrosEnablePlatformEncryptedHevc);
+#else
+  const bool allow_hevc = true;
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (allow_hevc && !enforce_codec_specificity) {
     // Remove any detected HEVC codec from the query to GetSupportsType.
     std::string filtered_codecs;
     std::vector<std::string> parsed_codec_ids;
diff --git a/third_party/blink/renderer/modules/webcodecs/BUILD.gn b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
index 1963b664..304465f 100644
--- a/third_party/blink/renderer/modules/webcodecs/BUILD.gn
+++ b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
@@ -10,6 +10,7 @@
 
 blink_modules_sources("webcodecs") {
   sources = [
+    "allow_shared_buffer_source_util.h",
     "audio_data.cc",
     "audio_data.h",
     "audio_data_attachment.cc",
diff --git a/third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h b/third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h
new file mode 100644
index 0000000..a0b13e46
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h
@@ -0,0 +1,42 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_ALLOW_SHARED_BUFFER_SOURCE_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_ALLOW_SHARED_BUFFER_SOURCE_UTIL_H_
+
+#include "base/containers/span.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybufferallowshared_arraybufferviewallowshared.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+
+namespace blink {
+
+using AllowSharedBufferSource =
+    V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared;
+
+// Helper function for turning various DOMArray-like things into a pointer+size.
+template <typename T>
+base::span<T> AsSpan(const AllowSharedBufferSource* buffer_union) {
+  switch (buffer_union->GetContentType()) {
+    case AllowSharedBufferSource::ContentType::kArrayBufferAllowShared: {
+      auto* buffer = buffer_union->GetAsArrayBufferAllowShared();
+      return (buffer && !buffer->IsDetached())
+                 ? base::span<T>(
+                       reinterpret_cast<T*>(buffer->DataMaybeShared()),
+                       buffer->ByteLength())
+                 : base::span<T>();
+    }
+    case AllowSharedBufferSource::ContentType::kArrayBufferViewAllowShared: {
+      auto* buffer = buffer_union->GetAsArrayBufferViewAllowShared().Get();
+      return (buffer && !buffer->IsDetached())
+                 ? base::span<T>(
+                       reinterpret_cast<T*>(buffer->BaseAddressMaybeShared()),
+                       buffer->byteLength())
+                 : base::span<T>();
+    }
+  }
+}
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_ALLOW_SHARED_BUFFER_SOURCE_UTIL_H_
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_data.cc b/third_party/blink/renderer/modules/webcodecs/audio_data.cc
index af193ac..ae702573 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_data.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_data.cc
@@ -11,7 +11,6 @@
 #include "media/base/sample_format.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_copy_to_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_init.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
@@ -120,19 +119,22 @@
     return;
   }
 
-  DOMArrayPiece source_data(init->data());
-  if (total_bytes > source_data.ByteLength()) {
+  auto data_wrapper = AsSpan<const uint8_t>(init->data());
+  if (!data_wrapper.data()) {
+    exception_state.ThrowTypeError("data is detached.");
+    return;
+  }
+  if (total_bytes > data_wrapper.size()) {
     exception_state.ThrowTypeError(
-        String::Format("`data` is too small: needs %u bytes, received %zu.",
-                       total_bytes, source_data.ByteLength()));
+        String::Format("data is too small: needs %u bytes, received %zu.",
+                       total_bytes, data_wrapper.size()));
     return;
   }
 
   std::vector<const uint8_t*> wrapped_data;
   if (media::IsInterleaved(media_format)) {
     // Interleaved data can directly added.
-    wrapped_data.push_back(
-        reinterpret_cast<const uint8_t*>(source_data.Bytes()));
+    wrapped_data.push_back(data_wrapper.data());
   } else {
     // Planar data needs one pointer per channel.
     wrapped_data.resize(init->numberOfChannels());
@@ -142,7 +144,7 @@
         media::SampleFormatToBytesPerChannel(media_format);
 
     const uint8_t* plane_start =
-        reinterpret_cast<const uint8_t*>(source_data.Bytes());
+        reinterpret_cast<const uint8_t*>(data_wrapper.data());
 
     for (unsigned ch = 0; ch < init->numberOfChannels(); ++ch)
       wrapped_data[ch] = plane_start + ch * plane_size_in_bytes;
@@ -290,7 +292,7 @@
   return allocation_size;
 }
 
-void AudioData::copyTo(const V8BufferSource* destination,
+void AudioData::copyTo(const AllowSharedBufferSource* destination,
                        AudioDataCopyToOptions* copy_to_options,
                        ExceptionState& exception_state) {
   if (!data_) {
@@ -306,8 +308,13 @@
     return;
 
   // Validate destination buffer.
-  DOMArrayPiece buffer(destination);
-  if (buffer.ByteLength() < static_cast<size_t>(copy_size_in_bytes)) {
+  auto dest_wrapper = AsSpan<uint8_t>(destination);
+  if (!dest_wrapper.data()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kConstraintError,
+                                      "destination is detached.");
+    return;
+  }
+  if (dest_wrapper.size() < static_cast<size_t>(copy_size_in_bytes)) {
     exception_state.ThrowDOMException(DOMExceptionCode::kConstraintError,
                                       "destination is not large enough.");
     return;
@@ -335,7 +342,7 @@
   const uint32_t channel = copy_to_options->planeIndex();
 
   uint8_t* data_start = data_->channel_data()[channel] + offset_in_bytes;
-  memcpy(buffer.Bytes(), data_start, copy_size_in_bytes);
+  memcpy(dest_wrapper.data(), data_start, copy_size_in_bytes);
 }
 
 void AudioData::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_data.h b/third_party/blink/renderer/modules/webcodecs/audio_data.h
index 6e3260a..01ee0f5 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_data.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_data.h
@@ -10,13 +10,13 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_sample_format.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
-
-class ExceptionState;
 class AudioDataInit;
 class AudioDataCopyToOptions;
+class ExceptionState;
 
 class MODULES_EXPORT AudioData final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -45,7 +45,7 @@
   int64_t timestamp() const;
 
   uint32_t allocationSize(AudioDataCopyToOptions*, ExceptionState&);
-  void copyTo(const V8BufferSource* destination,
+  void copyTo(const AllowSharedBufferSource* destination,
               AudioDataCopyToOptions*,
               ExceptionState&);
 
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_data.idl b/third_party/blink/renderer/modules/webcodecs/audio_data.idl
index fd4b369..17ae2889 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_data.idl
+++ b/third_party/blink/renderer/modules/webcodecs/audio_data.idl
@@ -15,7 +15,7 @@
   void close();
 
   [RaisesException] unsigned long allocationSize(AudioDataCopyToOptions options);
-  [RaisesException] void copyTo([AllowShared] BufferSource destination,
+  [RaisesException] void copyTo(AllowSharedBufferSource destination,
                                 AudioDataCopyToOptions options);
 
   readonly attribute AudioSampleFormat format;
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_data_init.idl b/third_party/blink/renderer/modules/webcodecs/audio_data_init.idl
index 3ba008c..7df6ec56 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_data_init.idl
+++ b/third_party/blink/renderer/modules/webcodecs/audio_data_init.idl
@@ -10,5 +10,5 @@
   required [EnforceRange] unsigned long numberOfFrames;
   required [EnforceRange] unsigned long numberOfChannels;
   required [EnforceRange] long long timestamp;  // microseconds
-  required BufferSource data;
+  required AllowSharedBufferSource data;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_data_test.cc b/third_party/blink/renderer/modules/webcodecs/audio_data_test.cc
index adcbaab..93ead97 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_data_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_data_test.cc
@@ -8,11 +8,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_copy_to_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_init.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -43,7 +43,7 @@
       ASSERT_NEAR(data[i], start_value + i * kIncrement, kEpsilon) << "i=" << i;
   }
 
-  V8BufferSource* CreateDefaultData() {
+  AllowSharedBufferSource* CreateDefaultData() {
     auto* buffer = DOMArrayBuffer::Create(kChannels * kFrames, sizeof(float));
     for (int ch = 0; ch < kChannels; ++ch) {
       float* plane_start =
@@ -52,10 +52,10 @@
         plane_start[i] = static_cast<float>((i + ch * kFrames) * kIncrement);
       }
     }
-    return MakeGarbageCollected<V8BufferSource>(buffer);
+    return MakeGarbageCollected<AllowSharedBufferSource>(buffer);
   }
 
-  AudioDataInit* CreateDefaultAudioDataInit(V8BufferSource* data) {
+  AudioDataInit* CreateDefaultAudioDataInit(AllowSharedBufferSource* data) {
     auto* audio_data_init = AudioDataInit::Create();
     audio_data_init->setData(data);
     audio_data_init->setTimestamp(kTimestampInMicroSeconds);
@@ -199,8 +199,9 @@
   auto* options = CreateCopyToOptions(/*index=*/0, /*offset=*/absl::nullopt,
                                       /*count=*/absl::nullopt);
 
-  V8BufferSource* small_dest = MakeGarbageCollected<V8BufferSource>(
-      DOMArrayBuffer::Create(kFrames - 1, sizeof(float)));
+  AllowSharedBufferSource* small_dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(
+          DOMArrayBuffer::Create(kFrames - 1, sizeof(float)));
 
   frame->copyTo(small_dest, options, scope.GetExceptionState());
 
@@ -214,7 +215,8 @@
                                       /*count=*/absl::nullopt);
 
   DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(kFrames, sizeof(float));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(data_copy);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy);
 
   // All frames should have been copied.
   frame->copyTo(dest, options, scope.GetExceptionState());
@@ -231,7 +233,8 @@
                                       /*count=*/absl::nullopt);
 
   DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(kFrames, sizeof(float));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(data_copy);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy);
 
   // All frames should have been copied.
   frame->copyTo(dest, options, scope.GetExceptionState());
@@ -252,7 +255,8 @@
 
   // |data_copy| is bigger than what we need, and that's ok.
   DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(kFrames, sizeof(float));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(data_copy);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy);
 
   // All frames should have been copied.
   frame->copyTo(dest, options, scope.GetExceptionState());
@@ -272,7 +276,8 @@
 
   DOMArrayBuffer* data_copy =
       DOMArrayBuffer::Create(kPartialFrameCount, sizeof(float));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(data_copy);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy);
 
   // All frames should have been copied.
   frame->copyTo(dest, options, scope.GetExceptionState());
@@ -290,7 +295,8 @@
 
   DOMArrayBuffer* data_copy =
       DOMArrayBuffer::Create(kPartialFrameCount, sizeof(float));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(data_copy);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy);
 
   // All frames should have been copied.
   frame->copyTo(dest, options, scope.GetExceptionState());
@@ -342,7 +348,8 @@
 
   DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(
       kPartialFrameCount * kInterleavedChannels, sizeof(uint16_t));
-  V8BufferSource* dest = MakeGarbageCollected<V8BufferSource>(data_copy);
+  AllowSharedBufferSource* dest =
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy);
 
   // All frames should have been copied.
   frame->copyTo(dest, options, scope.GetExceptionState());
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
index 57511f68..956a696 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
@@ -16,12 +16,12 @@
 #include "media/base/waiting.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_support.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/audio_data.h"
 #include "third_party/blink/renderer/modules/webcodecs/audio_decoder_broker.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_config_eval.h"
@@ -71,10 +71,13 @@
   copy->setSampleRate(config.sampleRate());
   copy->setNumberOfChannels(config.numberOfChannels());
   if (config.hasDescription()) {
-    DOMArrayPiece buffer(config.description());
-    DOMArrayBuffer* buffer_copy =
-        DOMArrayBuffer::Create(buffer.Data(), buffer.ByteLength());
-    copy->setDescription(MakeGarbageCollected<V8BufferSource>(buffer_copy));
+    auto desc_wrapper = AsSpan<const uint8_t>(config.description());
+    if (!desc_wrapper.empty()) {
+      DOMArrayBuffer* buffer_copy =
+          DOMArrayBuffer::Create(desc_wrapper.data(), desc_wrapper.size());
+      copy->setDescription(
+          MakeGarbageCollected<AllowSharedBufferSource>(buffer_copy));
+    }
   }
   return copy;
 }
@@ -197,10 +200,13 @@
 
   std::vector<uint8_t> extra_data;
   if (config.hasDescription()) {
-    DOMArrayPiece buffer(config.description());
-    uint8_t* start = static_cast<uint8_t*>(buffer.Data());
-    size_t size = buffer.ByteLength();
-    extra_data.assign(start, start + size);
+    // TODO(crbug.com/1179970): This should throw if description is detached.
+    auto desc_wrapper = AsSpan<const uint8_t>(config.description());
+    if (!desc_wrapper.empty()) {
+      const uint8_t* start = desc_wrapper.data();
+      const size_t size = desc_wrapper.size();
+      extra_data.assign(start, start + size);
+    }
   }
 
   media::ChannelLayout channel_layout =
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder_config.idl b/third_party/blink/renderer/modules/webcodecs/audio_decoder_config.idl
index fc3527f..62f7d7d497d 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder_config.idl
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder_config.idl
@@ -19,5 +19,5 @@
 
   // Optional byte data required to initialize audio decoders such as Vorbis
   // codebooks.
-  BufferSource description;
+  AllowSharedBufferSource description;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
index 0ef02bf..484e866 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
@@ -18,13 +18,13 @@
 #include "media/base/offloading_audio_encoder.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_encoder_support.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_metadata.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
@@ -331,7 +331,7 @@
       auto* desc_array_buf = DOMArrayBuffer::Create(codec_desc.value().data(),
                                                     codec_desc.value().size());
       decoder_config->setDescription(
-          MakeGarbageCollected<V8BufferSource>(desc_array_buf));
+          MakeGarbageCollected<AllowSharedBufferSource>(desc_array_buf));
     }
     metadata->setDecoderConfig(decoder_config);
   }
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.cc b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.cc
index 8a89165..1110581 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.cc
@@ -7,20 +7,17 @@
 #include <utility>
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_init.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
 EncodedAudioChunk* EncodedAudioChunk::Create(
     const EncodedAudioChunkInit* init) {
-  DOMArrayPiece piece(init->data());
-  auto buffer =
-      piece.ByteLength()
-          ? media::DecoderBuffer::CopyFrom(
-                reinterpret_cast<uint8_t*>(piece.Data()), piece.ByteLength())
-          : base::MakeRefCounted<media::DecoderBuffer>(0);
+  auto data_wrapper = AsSpan<const uint8_t>(init->data());
+  auto buffer = data_wrapper.empty()
+                    ? base::MakeRefCounted<media::DecoderBuffer>(0)
+                    : media::DecoderBuffer::CopyFrom(data_wrapper.data(),
+                                                     data_wrapper.size());
 
   // Clamp within bounds of our internal TimeDelta-based duration. See
   // media/base/timestamp_constants.h
@@ -68,21 +65,21 @@
   return buffer_->data_size();
 }
 
-void EncodedAudioChunk::copyTo(const V8BufferSource* destination,
+void EncodedAudioChunk::copyTo(const AllowSharedBufferSource* destination,
                                ExceptionState& exception_state) {
   // Validate destination buffer.
-  DOMArrayPiece dest_wrapper(destination);
-  if (dest_wrapper.IsDetached()) {
+  auto dest_wrapper = AsSpan<uint8_t>(destination);
+  if (!dest_wrapper.data()) {
     exception_state.ThrowTypeError("destination is detached.");
     return;
   }
-  if (dest_wrapper.ByteLength() < buffer_->data_size()) {
+  if (dest_wrapper.size() < buffer_->data_size()) {
     exception_state.ThrowTypeError("destination is not large enough.");
     return;
   }
 
   // Copy data.
-  memcpy(dest_wrapper.Bytes(), buffer_->data(), buffer_->data_size());
+  memcpy(dest_wrapper.data(), buffer_->data(), buffer_->data_size());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h
index 6163977..86c5ce9 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h
@@ -7,8 +7,8 @@
 
 #include "media/base/decoder_buffer.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
@@ -30,7 +30,7 @@
   int64_t timestamp() const;
   uint64_t byteLength() const;
   absl::optional<uint64_t> duration() const;
-  void copyTo(const V8BufferSource* destination,
+  void copyTo(const AllowSharedBufferSource* destination,
               ExceptionState& exception_state);
 
   scoped_refptr<media::DecoderBuffer> buffer() const { return buffer_; }
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.idl b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.idl
index ba212f77..103dfca6 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.idl
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.idl
@@ -17,5 +17,5 @@
   readonly attribute unsigned long byteLength;
   readonly attribute unsigned long long? duration;
   [RaisesException]
-  void copyTo([AllowShared] BufferSource destination);
+  void copyTo(AllowSharedBufferSource destination);
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.idl b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.idl
index c18f89b7..2d92fe05 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.idl
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.idl
@@ -8,5 +8,5 @@
   required EncodedAudioChunkType type;
   required [EnforceRange] long long timestamp; // microseconds
   [EnforceRange] unsigned long long duration;  // microseconds
-  required BufferSource data;
+  required AllowSharedBufferSource data;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc
index 2535d2c..4bb82cb2 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.cc
@@ -7,20 +7,17 @@
 #include <utility>
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
 EncodedVideoChunk* EncodedVideoChunk::Create(EncodedVideoChunkInit* init) {
-  DOMArrayPiece piece(init->data());
-  auto buffer =
-      piece.ByteLength()
-          ? media::DecoderBuffer::CopyFrom(
-                reinterpret_cast<uint8_t*>(piece.Data()), piece.ByteLength())
-          : base::MakeRefCounted<media::DecoderBuffer>(0);
+  auto data_wrapper = AsSpan<const uint8_t>(init->data());
+  auto buffer = data_wrapper.empty()
+                    ? base::MakeRefCounted<media::DecoderBuffer>(0)
+                    : media::DecoderBuffer::CopyFrom(data_wrapper.data(),
+                                                     data_wrapper.size());
 
   // Clamp within bounds of our internal TimeDelta-based duration. See
   // media/base/timestamp_constants.h
@@ -68,21 +65,21 @@
   return buffer_->data_size();
 }
 
-void EncodedVideoChunk::copyTo(const V8BufferSource* destination,
+void EncodedVideoChunk::copyTo(const AllowSharedBufferSource* destination,
                                ExceptionState& exception_state) {
   // Validate destination buffer.
-  DOMArrayPiece dest_wrapper(destination);
-  if (dest_wrapper.IsDetached()) {
+  auto dest_wrapper = AsSpan<uint8_t>(destination);
+  if (!dest_wrapper.data()) {
     exception_state.ThrowTypeError("destination is detached.");
     return;
   }
-  if (dest_wrapper.ByteLength() < buffer_->data_size()) {
+  if (dest_wrapper.size() < buffer_->data_size()) {
     exception_state.ThrowTypeError("destination is not large enough.");
     return;
   }
 
   // Copy data.
-  memcpy(dest_wrapper.Bytes(), buffer_->data(), buffer_->data_size());
+  memcpy(dest_wrapper.data(), buffer_->data(), buffer_->data_size());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h
index 3ca475c2..b89ccb62 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h
@@ -8,12 +8,11 @@
 #include "media/base/decoder_buffer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
-
 class EncodedVideoChunkInit;
 class ExceptionState;
 
@@ -30,7 +29,7 @@
   int64_t timestamp() const;
   absl::optional<uint64_t> duration() const;
   uint64_t byteLength() const;
-  void copyTo(const V8BufferSource* destination,
+  void copyTo(const AllowSharedBufferSource* destination,
               ExceptionState& exception_state);
 
   scoped_refptr<media::DecoderBuffer> buffer() const { return buffer_; }
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.idl b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.idl
index 2456deeb..353690d 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.idl
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.idl
@@ -19,5 +19,5 @@
   readonly attribute unsigned long byteLength;
 
   [RaisesException]
-  void copyTo([AllowShared] BufferSource destination);
+  void copyTo(AllowSharedBufferSource destination);
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.idl b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.idl
index 9c86828..03127ed8 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.idl
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_init.idl
@@ -8,5 +8,5 @@
   required EncodedVideoChunkType type;
   required [EnforceRange] long long timestamp; // microseconds
   [EnforceRange] unsigned long long duration;  // microseconds
-  required BufferSource data;
+  required AllowSharedBufferSource data;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc
index 84612680..78a183c 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoded_video_chunk_test.cc
@@ -6,8 +6,8 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -16,8 +16,8 @@
 
 class EncodedVideoChunkTest : public testing::Test {
  public:
-  V8BufferSource* StringToBuffer(std::string data) {
-    return MakeGarbageCollected<V8BufferSource>(
+  AllowSharedBufferSource* StringToBuffer(std::string data) {
+    return MakeGarbageCollected<AllowSharedBufferSource>(
         DOMArrayBuffer::Create(data.data(), data.size()));
   }
 
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
index 671ca91..3ec5d81 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_init.h"
@@ -25,6 +24,7 @@
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
@@ -58,7 +58,8 @@
   config->setCodec(proto.codec().c_str());
   DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(
       proto.description().data(), proto.description().size());
-  config->setDescription(MakeGarbageCollected<V8BufferSource>(data_copy));
+  config->setDescription(
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy));
   return config;
 }
 
@@ -71,7 +72,8 @@
 
   DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(
       proto.description().data(), proto.description().size());
-  config->setDescription(MakeGarbageCollected<V8BufferSource>(data_copy));
+  config->setDescription(
+      MakeGarbageCollected<AllowSharedBufferSource>(data_copy));
 
   return config;
 }
@@ -167,7 +169,7 @@
 
 EncodedVideoChunk* MakeEncodedVideoChunk(
     const wc_fuzzer::EncodedVideoChunk& proto) {
-  auto* data = MakeGarbageCollected<V8BufferSource>(
+  auto* data = MakeGarbageCollected<AllowSharedBufferSource>(
       DOMArrayBuffer::Create(proto.data().data(), proto.data().size()));
 
   auto* init = EncodedVideoChunkInit::Create();
@@ -183,7 +185,7 @@
 
 EncodedAudioChunk* MakeEncodedAudioChunk(
     const wc_fuzzer::EncodedAudioChunk& proto) {
-  auto* data = MakeGarbageCollected<V8BufferSource>(
+  auto* data = MakeGarbageCollected<AllowSharedBufferSource>(
       DOMArrayBuffer::Create(proto.data().data(), proto.data().size()));
 
   auto* init = EncodedAudioChunkInit::Create();
@@ -277,7 +279,7 @@
   init->setNumberOfChannels(proto.channels().size());
   init->setSampleRate(proto.sample_rate());
   init->setFormat(format);
-  init->setData(MakeGarbageCollected<V8BufferSource>(buffer));
+  init->setData(MakeGarbageCollected<AllowSharedBufferSource>(buffer));
 
   return AudioData::Create(init, IGNORE_EXCEPTION_FOR_TESTING);
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
index 3e81a58..e4b11dd 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
@@ -21,12 +21,13 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_color_space_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_support.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_config_eval.h"
 #include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
 #include "third_party/blink/renderer/modules/webcodecs/gpu_factories_retriever.h"
@@ -164,15 +165,21 @@
   return true;
 }
 
-VideoDecoderConfig* CopyConfig(const VideoDecoderConfig& config) {
+VideoDecoderConfig* CopyConfig(const VideoDecoderConfig& config,
+                               ExceptionState& exception_state) {
   VideoDecoderConfig* copy = VideoDecoderConfig::Create();
   copy->setCodec(config.codec());
 
   if (config.hasDescription()) {
-    DOMArrayPiece buffer(config.description());
+    auto desc_wrapper = AsSpan<const uint8_t>(config.description());
+    if (!desc_wrapper.data()) {
+      exception_state.ThrowTypeError("description is detached.");
+      return nullptr;
+    }
     DOMArrayBuffer* buffer_copy =
-        DOMArrayBuffer::Create(buffer.Data(), buffer.ByteLength());
-    copy->setDescription(MakeGarbageCollected<V8BufferSource>(buffer_copy));
+        DOMArrayBuffer::Create(desc_wrapper.data(), desc_wrapper.size());
+    copy->setDescription(
+        MakeGarbageCollected<AllowSharedBufferSource>(buffer_copy));
   }
 
   if (config.hasCodedWidth())
@@ -330,7 +337,12 @@
   // Accept all supported configs if we are not requiring hardware only.
   VideoDecoderSupport* support = VideoDecoderSupport::Create();
   support->setSupported(media::IsSupportedVideoType(video_type));
-  support->setConfig(CopyConfig(*config));
+
+  auto* config_copy = CopyConfig(*config, exception_state);
+  if (exception_state.HadException())
+    return ScriptPromise();
+
+  support->setConfig(config_copy);
   return ScriptPromise::Cast(script_state, ToV8(support, script_state));
 }
 
@@ -357,10 +369,14 @@
     return ScriptPromise();
   }
 
+  auto* config_copy = CopyConfig(*config, exception_state);
+  if (exception_state.HadException())
+    return ScriptPromise();
+
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
   VideoDecoderSupport* support = VideoDecoderSupport::Create();
-  support->setConfig(CopyConfig(*config));
+  support->setConfig(config_copy);
 
   RetrieveGpuFactoriesWithKnownDecoderSupport(CrossThreadBindOnce(
       &DecoderSupport_OnKnown, WrapCrossThreadPersistent(support),
@@ -398,13 +414,15 @@
   if (!IsValidConfig(config, video_type, out_console_message))
     return CodecConfigEval::kInvalid;
 
-  // TODO(sandersd): Can we allow shared ArrayBuffers?
   std::vector<uint8_t> extra_data;
   if (config.hasDescription()) {
-    DOMArrayPiece buffer(config.description());
-    uint8_t* start = static_cast<uint8_t*>(buffer.Data());
-    size_t size = buffer.ByteLength();
-    extra_data.assign(start, start + size);
+    // TODO(crbug.com/1179970): This should throw if description is detached.
+    auto desc_wrapper = AsSpan<const uint8_t>(config.description());
+    if (!desc_wrapper.empty()) {
+      const uint8_t* start = desc_wrapper.data();
+      const size_t size = desc_wrapper.size();
+      extra_data.assign(start, start + size);
+    }
   }
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl b/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl
index 3464290..9b6e2f5 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl
@@ -15,7 +15,7 @@
   // avcC, vpcC, or etc.
   // TODO(sandersd): Define what happens if the parsed description differs from
   // the metadata below.
-  BufferSource description;
+  AllowSharedBufferSource description;
 
   // Hint about the encoded size of the content.
   [EnforceRange] unsigned long codedWidth;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
index 30272fe..0dae13b 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
@@ -37,7 +37,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_avc_encoder_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_metadata.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_color_space_init.h"
@@ -50,6 +49,7 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_state_helper.h"
 #include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
 #include "third_party/blink/renderer/modules/webcodecs/gpu_factories_retriever.h"
@@ -836,7 +836,7 @@
       auto* desc_array_buf = DOMArrayBuffer::Create(codec_desc.value().data(),
                                                     codec_desc.value().size());
       decoder_config->setDescription(
-          MakeGarbageCollected<V8BufferSource>(desc_array_buf));
+          MakeGarbageCollected<AllowSharedBufferSource>(desc_array_buf));
     }
     metadata->setDecoderConfig(decoder_config);
   }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index 8a94630..c0a2671b 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -21,7 +21,6 @@
 #include "media/base/video_util.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybufferallowshared_arraybufferviewallowshared.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_plane_layout.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_cssimagevalue_htmlcanvaselement_htmlimageelement_htmlvideoelement_imagebitmap_offscreencanvas_svgimageelement_videoframe.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_color_space_init.h"
@@ -194,34 +193,6 @@
   }
 }
 
-// Helper function for turning various DOMArray-like things into a pointer+size.
-template <typename T>
-base::span<T> AsSpan(
-    const V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared*
-        buffer_union) {
-  switch (buffer_union->GetContentType()) {
-    case V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared::
-        ContentType::kArrayBufferAllowShared: {
-      auto* buffer = buffer_union->GetAsArrayBufferAllowShared();
-      return (buffer && !buffer->IsDetached())
-                 ? base::span<T>(
-                       reinterpret_cast<T*>(buffer->DataMaybeShared()),
-                       buffer->ByteLength())
-                 : base::span<T>();
-    }
-    case V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared::
-        ContentType::kArrayBufferViewAllowShared: {
-      auto* buffer = buffer_union->GetAsArrayBufferViewAllowShared().Get();
-      return (buffer && !buffer->IsDetached())
-                 ? base::span<T>(
-                       reinterpret_cast<T*>(buffer->BaseAddressMaybeShared()),
-                       buffer->byteLength())
-                 : base::span<T>();
-    }
-  }
-  return base::span<T>();
-}
-
 }  // namespace
 
 VideoFrame::VideoFrame(scoped_refptr<media::VideoFrame> frame,
@@ -438,11 +409,10 @@
           ExecutionContext::From(script_state)));
 }
 
-VideoFrame* VideoFrame::Create(
-    ScriptState* script_state,
-    const V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared* data,
-    const VideoFrameBufferInit* init,
-    ExceptionState& exception_state) {
+VideoFrame* VideoFrame::Create(ScriptState* script_state,
+                               const AllowSharedBufferSource* data,
+                               const VideoFrameBufferInit* init,
+                               ExceptionState& exception_state) {
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
 
   // Handle format; the string was validated by the V8 binding.
@@ -743,12 +713,10 @@
   return layout.min_buffer_size;
 }
 
-ScriptPromise VideoFrame::copyTo(
-    ScriptState* script_state,
-    const V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared*
-        destination,
-    VideoFrameCopyToOptions* options,
-    ExceptionState& exception_state) {
+ScriptPromise VideoFrame::copyTo(ScriptState* script_state,
+                                 const AllowSharedBufferSource* destination,
+                                 VideoFrameCopyToOptions* options,
+                                 ExceptionState& exception_state) {
   auto local_frame = handle_->frame();
   if (!local_frame) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.h b/third_party/blink/renderer/modules/webcodecs/video_frame.h
index 04dccc65..0debcc7 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.h
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap_source.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_image_source_util.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/webcodecs/allow_shared_buffer_source_util.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame_handle.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
@@ -40,7 +41,6 @@
 class VideoFrameBufferInit;
 class VideoFrameCopyToOptions;
 class VideoFrameInit;
-class V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared;
 
 class MODULES_EXPORT VideoFrame final : public ScriptWrappable,
                                         public CanvasImageSource,
@@ -66,11 +66,10 @@
                             const V8CanvasImageSource* source,
                             const VideoFrameInit* init,
                             ExceptionState& exception_state);
-  static VideoFrame* Create(
-      ScriptState*,
-      const V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared*,
-      const VideoFrameBufferInit*,
-      ExceptionState&);
+  static VideoFrame* Create(ScriptState*,
+                            const AllowSharedBufferSource*,
+                            const VideoFrameBufferInit*,
+                            ExceptionState&);
 
   absl::optional<V8VideoPixelFormat> format() const;
 
@@ -90,12 +89,10 @@
 
   uint32_t allocationSize(VideoFrameCopyToOptions* options, ExceptionState&);
 
-  ScriptPromise copyTo(
-      ScriptState* script_state,
-      const V8UnionArrayBufferAllowSharedOrArrayBufferViewAllowShared*
-          destination,
-      VideoFrameCopyToOptions* options,
-      ExceptionState& exception_state);
+  ScriptPromise copyTo(ScriptState* script_state,
+                       const AllowSharedBufferSource* destination,
+                       VideoFrameCopyToOptions* options,
+                       ExceptionState& exception_state);
 
   // Invalidates |handle_|, releasing underlying media::VideoFrame references.
   // This effectively "destroys" all frames sharing the same Handle.
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.idl b/third_party/blink/renderer/modules/webcodecs/video_frame.idl
index 4d1f464..98d0c70 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.idl
@@ -4,6 +4,8 @@
 
 // https://github.com/WICG/web-codecs
 
+typedef ([AllowShared] ArrayBuffer or [AllowShared] ArrayBufferView) AllowSharedBufferSource;
+
 [
     Exposed=(Window,DedicatedWorker),
     Serializable,
@@ -13,9 +15,7 @@
   constructor(CanvasImageSource source, optional VideoFrameInit init = {});
 
   [CallWith=ScriptState, RaisesException]
-  constructor(
-      ([AllowShared] ArrayBuffer or [AllowShared] ArrayBufferView) data,
-      VideoFrameBufferInit init);
+  constructor(AllowSharedBufferSource data, VideoFrameBufferInit init);
 
   readonly attribute VideoPixelFormat? format;
 
@@ -51,7 +51,7 @@
   // The format of the data is the same as this frame's |format|.
   [CallWith=ScriptState, RaisesException]
   Promise<sequence<PlaneLayout>> copyTo(
-      ([AllowShared] ArrayBuffer or [AllowShared] ArrayBufferView) destination,
+      AllowSharedBufferSource destination,
       optional VideoFrameCopyToOptions options = {});
 
   // Creates a copy of this VideoFrame, which needs to be independently closed.
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
index ac68f09..10e4675 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -726,13 +726,19 @@
     return false;
   }
 
+  // TODO(crbug.com/1197369): config color space based on image
   scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
       WebGPUMailboxTexture::FromStaticBitmapImage(
           GetDawnControlClient(), device_->GetHandle(),
           static_cast<WGPUTextureUsage>(WGPUTextureUsage_CopyDst |
                                         WGPUTextureUsage_CopySrc |
                                         WGPUTextureUsage_Sampled),
-          image);
+          image, CanvasColorSpace::kSRGB, image_info.colorType());
+
+  // Fail to associate staticBitmapImage to dawn resource.
+  if (!mailbox_texture) {
+    return false;
+  }
 
   WGPUTexture src_texture = mailbox_texture->GetTexture();
   DCHECK(src_texture != nullptr);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 40923ae..8a3e97c 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1891,6 +1891,8 @@
     "testing/layer_tree_host_embedder.h",
     "testing/message_loop_for_mojo.h",
     "testing/mock_context_lifecycle_notifier.h",
+    "testing/noop_web_url_loader.cc",
+    "testing/noop_web_url_loader.h",
     "testing/paint_property_test_helpers.h",
     "testing/paint_test_configurations.h",
     "testing/picture_matchers.cc",
diff --git a/third_party/blink/renderer/platform/DEPS b/third_party/blink/renderer/platform/DEPS
index efde8dfc..5076d74 100644
--- a/third_party/blink/renderer/platform/DEPS
+++ b/third_party/blink/renderer/platform/DEPS
@@ -30,12 +30,14 @@
     "+base/metrics/histogram_macros.h",
     "+base/metrics/histogram_samples.h",
     "+base/metrics/sparse_histogram.h",
+    "+base/no_destructor.h",
     "+base/numerics/checked_math.h",
     "+base/numerics/safe_conversions.h",
     "+base/process/memory.h",
     "+base/rand_util.h",
     "+base/run_loop.h",
     "+base/strings/pattern.h",
+    "+base/strings/string_split.h",
     "+base/strings/string_util.h",
     "+base/strings/stringprintf.h",
     "+base/synchronization/waitable_event.h",
diff --git a/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc b/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc
index 01cb98a..df8a6f50 100644
--- a/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc
+++ b/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc
@@ -49,7 +49,8 @@
 HRTFDatabaseLoader::CreateAndLoadAsynchronouslyIfNecessary(float sample_rate) {
   DCHECK(IsMainThread());
 
-  scoped_refptr<HRTFDatabaseLoader> loader = GetLoaderMap().at(sample_rate);
+  scoped_refptr<HRTFDatabaseLoader> loader =
+      GetLoaderMap().DeprecatedAtOrEmptyValue(sample_rate);
   if (loader) {
     DCHECK_EQ(sample_rate, loader->DatabaseSampleRate());
     return loader;
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc
index 00e0cdb8..0281384eb 100644
--- a/third_party/blink/renderer/platform/fonts/font.cc
+++ b/third_party/blink/renderer/platform/fonts/font.cc
@@ -535,7 +535,7 @@
 void Font::WillUseFontData(const String& text) const {
   const FontFamily& family = GetFontDescription().Family();
   if (font_fallback_list_ && font_fallback_list_->GetFontSelector() &&
-      !family.FamilyIsEmpty())
+      !family.Family().IsEmpty())
     font_fallback_list_->GetFontSelector()->WillUseFontData(
         GetFontDescription(), family.Family(), text);
 }
diff --git a/third_party/blink/renderer/platform/fonts/font_description.cc b/third_party/blink/renderer/platform/fonts/font_description.cc
index 911450a..98f86c9 100644
--- a/third_party/blink/renderer/platform/fonts/font_description.cc
+++ b/third_party/blink/renderer/platform/fonts/font_description.cc
@@ -388,7 +388,7 @@
   unsigned hash = StyleHashWithoutFamilyList();
   for (const FontFamily* family = &family_list_; family;
        family = family->Next()) {
-    if (!family->Family().length())
+    if (family->Family().IsEmpty())
       continue;
     WTF::AddIntToHash(hash, WTF::AtomicStringHash::GetHash(family->Family()));
   }
diff --git a/third_party/blink/renderer/platform/fonts/font_fallback_list.cc b/third_party/blink/renderer/platform/fonts/font_fallback_list.cc
index 41492a0..5b836e5 100644
--- a/third_party/blink/renderer/platform/fonts/font_fallback_list.cc
+++ b/third_party/blink/renderer/platform/fonts/font_fallback_list.cc
@@ -162,7 +162,7 @@
 
   for (; curr_family; curr_family = curr_family->Next()) {
     family_index_++;
-    if (curr_family->Family().length()) {
+    if (!curr_family->Family().IsEmpty()) {
       scoped_refptr<FontData> result;
       if (GetFontSelector()) {
         result = GetFontSelector()->GetFontData(font_description,
@@ -214,7 +214,7 @@
   FallbackListCompositeKey key(font_description);
   const FontFamily* current_family = &font_description.Family();
   while (current_family) {
-    if (current_family->Family().length()) {
+    if (!current_family->Family().IsEmpty()) {
       FontFaceCreationParams params(
           AdjustFamilyNameToAvoidUnsupportedFonts(current_family->Family()));
       scoped_refptr<FontData> result;
diff --git a/third_party/blink/renderer/platform/fonts/font_family.h b/third_party/blink/renderer/platform/fonts/font_family.h
index 947b2952..fa69d30 100644
--- a/third_party/blink/renderer/platform/fonts/font_family.h
+++ b/third_party/blink/renderer/platform/fonts/font_family.h
@@ -44,7 +44,6 @@
 
   void SetFamily(const AtomicString& family) { family_ = family; }
   const AtomicString& Family() const { return family_; }
-  bool FamilyIsEmpty() const { return family_.IsEmpty(); }
 
   const FontFamily* Next() const;
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
index ac078f6..470c2d8f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -39,10 +39,10 @@
     LayerTreeFlags flags,
     const cc::Layer& layer,
     JSONObject& json) const {
-#if DCHECK_IS_ON()
+#if EXPENSIVE_DCHECKS_ARE_ON()
   if (flags & kLayerTreeIncludesDebugInfo)
     json.SetValue("paintChunkContents", paint_chunk_debug_data_->Clone());
-#endif
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
 
   if ((flags & (kLayerTreeIncludesInvalidations |
                 kLayerTreeIncludesDetailedInvalidations)) &&
@@ -70,7 +70,7 @@
   else
     id_ = absl::nullopt;
 
-#if DCHECK_IS_ON()
+#if EXPENSIVE_DCHECKS_ARE_ON()
   paint_chunk_debug_data_ = std::make_unique<JSONArray>();
   for (auto it = paint_chunks.begin(); it != paint_chunks.end(); ++it) {
     auto json = std::make_unique<JSONObject>();
@@ -80,7 +80,7 @@
                                        DisplayItemList::kCompact));
     paint_chunk_debug_data_->PushObject(std::move(json));
   }
-#endif
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
 
   // The raster invalidator will only handle invalidations within a cc::Layer so
   // we need this invalidation if the layer's properties have changed.
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
index 05dfcdf..8f8964b 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -75,9 +75,9 @@
   PropertyTreeState layer_state_;
 
   String debug_name_;
-#if DCHECK_IS_ON()
+#if EXPENSIVE_DCHECKS_ARE_ON()
   std::unique_ptr<JSONArray> paint_chunk_debug_data_;
-#endif
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index d5b91eb5..0d01b1f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -62,7 +62,7 @@
 
 class MockScrollCallbacks : public CompositorScrollCallbacks {
  public:
-  MOCK_METHOD3(DidScroll,
+  MOCK_METHOD3(DidCompositorScroll,
                void(CompositorElementId,
                     const gfx::ScrollOffset&,
                     const absl::optional<cc::TargetSnapAreaElementIds>&));
@@ -1115,9 +1115,10 @@
   EXPECT_EQ(scroll_layer->scroll_tree_index(), scroll_node.id);
 
   absl::optional<cc::TargetSnapAreaElementIds> targets;
-  EXPECT_CALL(ScrollCallbacks(), DidScroll(scroll_node.element_id,
-                                           gfx::ScrollOffset(1, 2), targets));
-  GetPropertyTrees().scroll_tree.NotifyDidScroll(
+  EXPECT_CALL(ScrollCallbacks(),
+              DidCompositorScroll(scroll_node.element_id,
+                                  gfx::ScrollOffset(1, 2), targets));
+  GetPropertyTrees().scroll_tree.NotifyDidCompositorScroll(
       scroll_node.element_id, gfx::ScrollOffset(1, 2), targets);
 
   EXPECT_CALL(ScrollCallbacks(),
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc
index afcac98..5a1fec4f 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc
@@ -403,11 +403,12 @@
                 *reinterpret_cast<const volatile gpu::Mailbox*>(mailbox_bytes));
           })));
 
+  SkImageInfo image_info = bitmap->PaintImageForCurrentFrame().GetSkImageInfo();
   scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
       WebGPUMailboxTexture::FromStaticBitmapImage(
-          dawn_control_client_, fake_device_, WGPUTextureUsage_CopySrc, bitmap);
+          dawn_control_client_, fake_device_, WGPUTextureUsage_CopySrc, bitmap,
+          CanvasColorSpace::kSRGB, image_info.colorType());
 
-  EXPECT_TRUE(mailbox == bitmap->GetMailboxHolder().mailbox);
   EXPECT_NE(mailbox_texture->GetTexture(), nullptr);
   EXPECT_EQ(mailbox_texture->GetTextureIdForTest(), 1u);
   EXPECT_EQ(mailbox_texture->GetTextureGenerationForTest(), 1u);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
index cda6afa..ea25005 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
@@ -6,6 +6,7 @@
 
 #include "gpu/command_buffer/client/webgpu_interface.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 
 namespace blink {
@@ -15,16 +16,56 @@
     scoped_refptr<DawnControlClientHolder> dawn_control_client,
     WGPUDevice device,
     WGPUTextureUsage usage,
-    scoped_refptr<StaticBitmapImage> image) {
+    scoped_refptr<StaticBitmapImage> image,
+    CanvasColorSpace color_space,
+    SkColorType color_type) {
   DCHECK(image->IsTextureBacked());
-  auto finished_access_callback =
-      WTF::Bind(&StaticBitmapImage::UpdateSyncToken, WTF::RetainedRef(image));
 
-  return base::AdoptRef(new WebGPUMailboxTexture(
-      std::move(dawn_control_client), device, usage,
-      image->GetMailboxHolder().mailbox, image->GetMailboxHolder().sync_token,
-      std::move(finished_access_callback),
-      /*recyclable_canvas_resource=*/nullptr));
+  // TODO(crbugs.com/1217160) Mac uses IOSurface in SharedImageBackingGLImage
+  // which can be shared to dawn directly aftter passthrough command buffer
+  // supported on mac os.
+  // We should wrap the StaticBitmapImage directly for mac when passthrough
+  // command buffer has been supported.
+
+  // If the context is lost, the resource provider would be invalid.
+  auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper();
+  if (!context_provider_wrapper ||
+      context_provider_wrapper->ContextProvider()->IsContextLost())
+    return nullptr;
+
+  // Keep the same config as source image.
+  const CanvasResourceParams params(
+      color_space, color_type,
+      image->IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
+
+  // Get a recyclable resource for producing WebGPU-compatible shared images.
+  std::unique_ptr<RecyclableCanvasResource> recyclable_canvas_resource =
+      dawn_control_client->GetOrCreateCanvasResource(image->Size(), params,
+                                                     image->IsOriginTopLeft());
+
+  // Fallback to unstable intermediate resource copy path.
+  if (!recyclable_canvas_resource) {
+    auto finished_access_callback =
+        WTF::Bind(&StaticBitmapImage::UpdateSyncToken, WTF::RetainedRef(image));
+
+    return base::AdoptRef(new WebGPUMailboxTexture(
+        std::move(dawn_control_client), device, usage,
+        image->GetMailboxHolder().mailbox, image->GetMailboxHolder().sync_token,
+        std::move(finished_access_callback),
+        /*recyclable_canvas_resource=*/nullptr));
+  }
+
+  CanvasResourceProvider* resource_provider =
+      recyclable_canvas_resource->resource_provider();
+  DCHECK(resource_provider);
+
+  if (!image->CopyToResourceProvider(resource_provider)) {
+    return nullptr;
+  }
+
+  return WebGPUMailboxTexture::FromCanvasResource(
+      dawn_control_client, device, usage,
+      std::move(recyclable_canvas_resource));
 }
 
 // static
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h
index d99bde1..b4270068 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h
@@ -28,7 +28,9 @@
       scoped_refptr<DawnControlClientHolder> dawn_control_client,
       WGPUDevice device,
       WGPUTextureUsage usage,
-      scoped_refptr<StaticBitmapImage> image);
+      scoped_refptr<StaticBitmapImage> image,
+      CanvasColorSpace color_space,
+      SkColorType color_type);
 
   static scoped_refptr<WebGPUMailboxTexture> FromCanvasResource(
       scoped_refptr<DawnControlClientHolder> dawn_control_client,
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 5314fc6..7091f84 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -277,6 +277,7 @@
 bool GraphicsLayer::PaintRecursively(
     GraphicsContext& context,
     Vector<PreCompositedLayerInfo>& pre_composited_layers,
+    PaintController::CycleScope& cycle_scope,
     PaintBenchmarkMode benchmark_mode) {
   bool repainted = false;
   ForAllGraphicsLayers(
@@ -286,7 +287,7 @@
           layer.ClearPaintStateRecursively();
           return false;
         }
-        layer.Paint(pre_composited_layers, benchmark_mode);
+        layer.Paint(pre_composited_layers, benchmark_mode, &cycle_scope);
         repainted |= layer.repainted_;
         return true;
       },
@@ -313,11 +314,14 @@
 
 void GraphicsLayer::PaintForTesting(const IntRect& interest_rect) {
   Vector<PreCompositedLayerInfo> pre_composited_layers;
-  Paint(pre_composited_layers, PaintBenchmarkMode::kNormal, &interest_rect);
+  PaintController::CycleScope cycle_scope;
+  Paint(pre_composited_layers, PaintBenchmarkMode::kNormal, &cycle_scope,
+        &interest_rect);
 }
 
 void GraphicsLayer::Paint(Vector<PreCompositedLayerInfo>& pre_composited_layers,
                           PaintBenchmarkMode benchmark_mode,
+                          PaintController::CycleScope* cycle_scope,
                           const IntRect* interest_rect) {
   repainted_ = false;
 
@@ -351,6 +355,8 @@
   }
 
   auto& paint_controller = GetPaintController();
+  if (cycle_scope)
+    cycle_scope->AddController(paint_controller);
 
   absl::optional<PaintChunkSubset> previous_chunks;
   if (ShouldCreateLayersAfterPaint())
@@ -365,7 +371,7 @@
                 paint_controller.ClientCacheIsValid(*this) &&
                 previous_interest_rect_ == new_interest_rect;
   if (!cached) {
-    paint_controller.ReserveCapacity();
+    paint_controller.MarkClientForValidation(*this);
     GraphicsContext context(paint_controller);
     DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
     paint_controller.UpdateCurrentPaintChunkProperties(
@@ -377,9 +383,6 @@
     previous_interest_rect_ = new_interest_rect;
     client_.PaintContents(this, context, painting_phase_, new_interest_rect);
     paint_controller.CommitNewDisplayItems();
-    // TODO(wangxianzhu): Remove this and friend class in DisplayItemClient
-    // when unifying PaintController.
-    Validate();
     DVLOG(2) << "Painted GraphicsLayer: " << DebugName()
              << " paintable region: " << PaintableRegion().ToString();
   }
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 694ae97..5ddf1add 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -173,6 +173,7 @@
   // Returns true if any layer is repainted.
   bool PaintRecursively(GraphicsContext&,
                         Vector<PreCompositedLayerInfo>&,
+                        PaintController::CycleScope& cycle_scope,
                         PaintBenchmarkMode = PaintBenchmarkMode::kNormal);
 
   PaintController& GetPaintController() const;
@@ -239,6 +240,7 @@
   void ClearPaintStateRecursively();
   void Paint(Vector<PreCompositedLayerInfo>&,
              PaintBenchmarkMode,
+             PaintController::CycleScope*,
              const IntRect* interest_rect = nullptr);
 
   // Adds a child without calling NotifyChildListChange(), so that adding
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc b/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
index 3856e9a..af4b9ca7 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
@@ -100,12 +100,14 @@
   GraphicsContext context(GetPaintController());
   client.SetNeedsRepaint(true);
   Vector<PreCompositedLayerInfo> pre_composited_layers;
-  EXPECT_TRUE(root.PaintRecursively(context, pre_composited_layers));
+  {
+    PaintController::CycleScope cycle_scope;
+    EXPECT_TRUE(
+        root.PaintRecursively(context, pre_composited_layers, cycle_scope));
+  }
   EXPECT_TRUE(root.Repainted());
-  root.GetPaintController().FinishCycle();
   EXPECT_FALSE(layer1.Repainted());
   EXPECT_TRUE(layer2.Repainted());
-  layer2.GetPaintController().FinishCycle();
 
   HitTestData hit_test_data;
   hit_test_data.touch_action_rects = {{IntRect(1, 2, 3, 4)}};
@@ -129,7 +131,11 @@
   // Paint again with nothing changed.
   client.SetNeedsRepaint(false);
   pre_composited_layers.clear();
-  EXPECT_FALSE(root.PaintRecursively(context, pre_composited_layers));
+  {
+    PaintController::CycleScope cycle_scope;
+    EXPECT_FALSE(
+        root.PaintRecursively(context, pre_composited_layers, cycle_scope));
+  }
   EXPECT_FALSE(root.Repainted());
   EXPECT_FALSE(layer1.Repainted());
   EXPECT_FALSE(layer2.Repainted());
@@ -138,10 +144,13 @@
   // Paint again with layer1 drawing content.
   layer1.SetDrawsContent(true);
   pre_composited_layers.clear();
-  EXPECT_TRUE(root.PaintRecursively(context, pre_composited_layers));
+  {
+    PaintController::CycleScope cycle_scope;
+    EXPECT_TRUE(
+        root.PaintRecursively(context, pre_composited_layers, cycle_scope));
+  }
   EXPECT_FALSE(root.Repainted());
   EXPECT_TRUE(layer1.Repainted());
-  layer1.GetPaintController().FinishCycle();
   EXPECT_FALSE(layer2.Repainted());
 
   EXPECT_EQ(3u, pre_composited_layers.size());
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_client.h b/third_party/blink/renderer/platform/graphics/paint/display_item_client.h
index 24fe02f4..9023fe2 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item_client.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item_client.h
@@ -27,7 +27,10 @@
 // dereferenced unless we can make sure the client is still alive.
 class PLATFORM_EXPORT DisplayItemClient {
  public:
-  DisplayItemClient() {
+  DisplayItemClient()
+      : paint_invalidation_reason_(
+            static_cast<uint8_t>(PaintInvalidationReason::kJustCreated)),
+        marked_for_validation_(0) {
 #if DCHECK_IS_ON()
     OnCreate();
 #endif
@@ -73,30 +76,32 @@
         // However, kUncacheable overwrites any other reason.
         reason != PaintInvalidationReason::kUncacheable)
       return;
-    paint_invalidation_reason_ = reason;
+    paint_invalidation_reason_ = static_cast<uint8_t>(reason);
   }
 
   PaintInvalidationReason GetPaintInvalidationReason() const {
-    return paint_invalidation_reason_;
+    return static_cast<PaintInvalidationReason>(paint_invalidation_reason_);
   }
 
   // A client is considered "just created" if its display items have never been
   // validated by any PaintController since it's created.
   bool IsJustCreated() const {
-    return paint_invalidation_reason_ == PaintInvalidationReason::kJustCreated;
+    return GetPaintInvalidationReason() ==
+           PaintInvalidationReason::kJustCreated;
   }
 
   // Whether the client is cacheable. The uncacheable status is set when the
   // client produces any display items that skipped caching of any
   // PaintController.
   bool IsCacheable() const {
-    return paint_invalidation_reason_ != PaintInvalidationReason::kUncacheable;
+    return GetPaintInvalidationReason() !=
+           PaintInvalidationReason::kUncacheable;
   }
 
   // True if the client's display items are cached in PaintControllers without
   // needing to update.
   bool IsValid() const {
-    return paint_invalidation_reason_ == PaintInvalidationReason::kNone;
+    return GetPaintInvalidationReason() == PaintInvalidationReason::kNone;
   }
 
   String ToString() const;
@@ -104,11 +109,15 @@
  private:
   friend class FakeDisplayItemClient;
   friend class ObjectPaintInvalidatorTest;
+  friend class PaintChunker;
   friend class PaintController;
-  friend class GraphicsLayer;  // Temporary for Validate().
 
+  void MarkForValidation() const { marked_for_validation_ = 1; }
+  bool IsMarkedForValidation() const { return marked_for_validation_; }
   void Validate() const {
-    paint_invalidation_reason_ = PaintInvalidationReason::kNone;
+    paint_invalidation_reason_ =
+        static_cast<uint8_t>(PaintInvalidationReason::kNone);
+    marked_for_validation_ = 0;
   }
 
 #if DCHECK_IS_ON()
@@ -116,8 +125,8 @@
   void OnDestroy();
 #endif
 
-  mutable PaintInvalidationReason paint_invalidation_reason_ =
-      PaintInvalidationReason::kJustCreated;
+  mutable uint8_t paint_invalidation_reason_ : 7;
+  mutable uint8_t marked_for_validation_ : 1;
 };
 
 inline bool operator==(const DisplayItemClient& client1,
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc b/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc
index 0a9925b6e..f07e50d 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc
@@ -22,18 +22,7 @@
  protected:
   DisplayItemRasterInvalidatorTest() = default;
 
-  Vector<RasterInvalidationInfo> GenerateRasterInvalidations() {
-    GetPaintController().CommitNewDisplayItems();
-    invalidator_.Generate(
-        base::DoNothing(),
-        PaintChunkSubset(GetPaintController().GetPaintArtifactShared()),
-        // The layer bounds are big enough not to clip display item raster
-        // invalidation rects in the tests.
-        FloatPoint(), IntSize(20000, 20000), PropertyTreeState::Root());
-    GetPaintController().FinishCycle();
-    for (auto& chunk : GetPaintController().PaintChunks())
-      chunk.properties.ClearChangedTo(PropertyTreeState::Root());
-
+  Vector<RasterInvalidationInfo> GetRasterInvalidations() {
     if (invalidator_.GetTracking())
       return invalidator_.GetTracking()->Invalidations();
     return Vector<RasterInvalidationInfo>();
@@ -44,6 +33,29 @@
   RasterInvalidator invalidator_;
 };
 
+class RasterInvalidationCycleScope : public PaintController::CycleScope {
+ public:
+  RasterInvalidationCycleScope(PaintController& controller,
+                               RasterInvalidator& invalidator)
+      : PaintController::CycleScope(controller), invalidator_(invalidator) {}
+  ~RasterInvalidationCycleScope() {
+    for (auto* controller : controllers_) {
+      controller->CommitNewDisplayItems();
+      invalidator_.Generate(
+          base::DoNothing(),
+          PaintChunkSubset(controller->GetPaintArtifactShared()),
+          // The layer bounds are big enough not to clip display item raster
+          // invalidation rects in the tests.
+          FloatPoint(), IntSize(20000, 20000), PropertyTreeState::Root());
+      for (auto& chunk : controller->PaintChunks())
+        chunk.properties.ClearChangedTo(PropertyTreeState::Root());
+    }
+  }
+
+ private:
+  RasterInvalidator& invalidator_;
+};
+
 INSTANTIATE_PAINT_TEST_SUITE_P(DisplayItemRasterInvalidatorTest);
 
 TEST_P(DisplayItemRasterInvalidatorTest,
@@ -52,20 +64,27 @@
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, first, kForegroundType, IntRect(100, 150, 300, 300));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, first, kForegroundType, IntRect(100, 150, 300, 300));
+  }
 
   first.Invalidate(PaintInvalidationReason::kStyle);
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, first, kForegroundType, IntRect(100, 150, 300, 300));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, first, kForegroundType, IntRect(100, 150, 300, 300));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &first, "first", IntRect(100, 100, 300, 350),
                   PaintInvalidationReason::kStyle}));
@@ -77,21 +96,28 @@
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, first, kForegroundType, IntRect(100, 150, 300, 300));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, first, kForegroundType, IntRect(100, 150, 300, 300));
+  }
 
   first.Invalidate(PaintInvalidationReason::kStyle);
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(200, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, first, kForegroundType, IntRect(200, 150, 300, 300));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(200, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, first, kForegroundType, IntRect(200, 150, 300, 300));
+  }
 
   EXPECT_THAT(
-      GenerateRasterInvalidations(),
+      GetRasterInvalidations(),
       UnorderedElementsAre(
           RasterInvalidationInfo{&first, "first", IntRect(100, 100, 300, 350),
                                  PaintInvalidationReason::kStyle},
@@ -105,18 +131,25 @@
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &second, "second", IntRect(100, 100, 200, 200),
                   PaintInvalidationReason::kDisappeared}));
@@ -129,25 +162,32 @@
   FakeDisplayItemClient unaffected("unaffected");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+    DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+    DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &first, "first", IntRect(100, 100, 100, 100),
                   PaintInvalidationReason::kReordered}));
@@ -160,20 +200,27 @@
   FakeDisplayItemClient unaffected("unaffected");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  first.Invalidate(PaintInvalidationReason::kOutline);
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    first.Invalidate(PaintInvalidationReason::kOutline);
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &first, "first", IntRect(100, 100, 100, 100),
                   PaintInvalidationReason::kOutline}));
@@ -186,20 +233,27 @@
   FakeDisplayItemClient unaffected("unaffected");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  second.Invalidate(PaintInvalidationReason::kOutline);
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    second.Invalidate(PaintInvalidationReason::kOutline);
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &second, "second", IntRect(100, 100, 50, 200),
                   PaintInvalidationReason::kOutline}));
@@ -212,21 +266,28 @@
   FakeDisplayItemClient unaffected("unaffected");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  first.Invalidate(PaintInvalidationReason::kIncremental);
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    first.Invalidate(PaintInvalidationReason::kIncremental);
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+  }
 
   // Incremental invalidation is not applicable when the item is reordered.
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &first, "first", IntRect(100, 100, 100, 100),
                   PaintInvalidationReason::kReordered}));
@@ -239,18 +300,25 @@
   FakeDisplayItemClient third("third");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, third, kBackgroundType, IntRect(125, 100, 200, 50));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, third, kBackgroundType, IntRect(125, 100, 200, 50));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &third, "third", IntRect(125, 100, 200, 50),
                   PaintInvalidationReason::kAppeared}));
@@ -266,24 +334,31 @@
   }
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
 
-  for (auto& client : clients)
-    DrawRect(context, *client, kBackgroundType, IntRect(initial_rect));
-  GenerateRasterInvalidations();
-
-  invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  IntRect visual_rects[] = {
-      IntRect(100, 100, 150, 100), IntRect(100, 100, 100, 150),
-      IntRect(100, 100, 150, 80),  IntRect(100, 100, 80, 150),
-      IntRect(100, 100, 150, 150), IntRect(100, 100, 80, 80)};
-  for (size_t i = 0; i < base::size(clients); i++) {
-    clients[i]->Invalidate(PaintInvalidationReason::kIncremental);
-    DrawRect(context, *clients[i], kBackgroundType, IntRect(visual_rects[i]));
+    for (auto& client : clients)
+      DrawRect(context, *client, kBackgroundType, IntRect(initial_rect));
   }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  invalidator_.SetTracksRasterInvalidations(true);
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    IntRect visual_rects[] = {
+        IntRect(100, 100, 150, 100), IntRect(100, 100, 100, 150),
+        IntRect(100, 100, 150, 80),  IntRect(100, 100, 80, 150),
+        IntRect(100, 100, 150, 150), IntRect(100, 100, 80, 80)};
+    for (size_t i = 0; i < base::size(clients); i++) {
+      clients[i]->Invalidate(PaintInvalidationReason::kIncremental);
+      DrawRect(context, *clients[i], kBackgroundType, IntRect(visual_rects[i]));
+    }
+  }
+
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(
                   RasterInvalidationInfo{clients[0].get(), "0",
                                          IntRect(200, 100, 50, 100),
@@ -324,23 +399,30 @@
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
-  DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
+    DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  first.Invalidate();
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
-  DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
-  EXPECT_EQ(0u, NumCachedNewItems());
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    first.Invalidate();
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
+    DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
+    EXPECT_EQ(0u, NumCachedNewItems());
+  }
 
   EXPECT_THAT(
-      GenerateRasterInvalidations(),
+      GetRasterInvalidations(),
       UnorderedElementsAre(
           RasterInvalidationInfo{&first, "first", IntRect(100, 100, 150, 150),
                                  PaintInvalidationReason::kAppeared},
@@ -351,11 +433,15 @@
   invalidator_.SetTracksRasterInvalidations(false);
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
-  DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
+    DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &first, "first", IntRect(100, 100, 150, 150),
                   PaintInvalidationReason::kDisappeared}));
@@ -367,22 +453,29 @@
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  first.Invalidate();
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(150, 150, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(150, 150, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
-  DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    first.Invalidate();
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(150, 150, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(150, 150, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
+    DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
+  }
 
   EXPECT_THAT(
-      GenerateRasterInvalidations(),
+      GetRasterInvalidations(),
       UnorderedElementsAre(
           RasterInvalidationInfo{&first, "first", IntRect(100, 100, 150, 150),
                                  PaintInvalidationReason::kFull},
@@ -391,14 +484,18 @@
   invalidator_.SetTracksRasterInvalidations(false);
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  first.Invalidate();
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    first.Invalidate();
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+  }
 
   EXPECT_THAT(
-      GenerateRasterInvalidations(),
+      GetRasterInvalidations(),
       UnorderedElementsAre(
           RasterInvalidationInfo{&first, "first", IntRect(100, 100, 150, 150),
                                  PaintInvalidationReason::kFull},
@@ -414,31 +511,38 @@
   FakeDisplayItemClient content2("content2");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+  }
 
   // Simulate the situation when |container1| gets a z-index that is greater
   // than that of |container2|.
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(
                   RasterInvalidationInfo{&container1, "container1",
                                          IntRect(100, 100, 100, 100),
@@ -456,32 +560,39 @@
   FakeDisplayItemClient content2("content2");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  // Simulate the situation when |container1| gets a z-index that is greater
-  // than that of |container2|, and |container1| is invalidated.
-  container2.Invalidate();
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    // Simulate the situation when |container1| gets a z-index that is greater
+    // than that of |container2|, and |container1| is invalidated.
+    container2.Invalidate();
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(
                   RasterInvalidationInfo{&container1, "container1",
                                          IntRect(100, 100, 100, 100),
@@ -512,28 +623,35 @@
 
   PaintChunk::Id container1_id(container1, kBackgroundType);
   PaintChunk::Id container2_id(container2, kBackgroundType);
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id,
-                                                         container1_properties);
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id,
-                                                         container2_properties);
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container1_id, container1_properties);
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container2_id, container2_properties);
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+  }
 
   // Move content2 into container1, without invalidation.
   invalidator_.SetTracksRasterInvalidations(true);
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id,
-                                                         container1_properties);
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id,
-                                                         container2_properties);
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container1_id, container1_properties);
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container2_id, container2_properties);
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(
                   RasterInvalidationInfo{&content2, "content2",
                                          IntRect(100, 200, 50, 200),
@@ -548,50 +666,60 @@
   FakeDisplayItemClient multicol("multicol");
   FakeDisplayItemClient content("content");
   GraphicsContext context(GetPaintController());
-
-  InitRootChunk();
   IntRect rect1(100, 100, 50, 50);
   IntRect rect2(150, 100, 50, 50);
   IntRect rect3(200, 100, 50, 50);
 
-  DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect1);
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect1);
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  // Draw again with nothing invalidated.
-  EXPECT_TRUE(ClientCacheIsValid(multicol));
-  DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    // Draw again with nothing invalidated.
+    EXPECT_TRUE(ClientCacheIsValid(multicol));
+    DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
 
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect1);
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect1);
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+  }
 
-  EXPECT_THAT(GenerateRasterInvalidations(),
+  EXPECT_THAT(GetRasterInvalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   &content, "content", UnionRect(rect1, rect2),
                   PaintInvalidationReason::kUncacheable}));
   invalidator_.SetTracksRasterInvalidations(false);
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  // Now the multicol becomes 3 columns and repaints.
-  multicol.Invalidate();
-  DrawRect(context, multicol, kBackgroundType, IntRect(100, 100, 100, 100));
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    // Now the multicol becomes 3 columns and repaints.
+    multicol.Invalidate();
+    DrawRect(context, multicol, kBackgroundType, IntRect(100, 100, 100, 100));
 
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect1);
-  DrawRect(context, content, kForegroundType, rect2);
-  DrawRect(context, content, kForegroundType, rect3);
-  GetPaintController().EndSkippingCache();
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect1);
+    DrawRect(context, content, kForegroundType, rect2);
+    DrawRect(context, content, kForegroundType, rect3);
+    GetPaintController().EndSkippingCache();
+  }
 
   EXPECT_THAT(
-      GenerateRasterInvalidations(),
+      GetRasterInvalidations(),
       UnorderedElementsAre(
           RasterInvalidationInfo{&multicol, "multicol",
                                  IntRect(100, 200, 100, 100),
@@ -613,25 +741,32 @@
   IntRect rect2(150, 100, 50, 50);
   IntRect rect3(200, 100, 50, 50);
 
-  InitRootChunk();
-  DrawRect(context, content, kBackgroundType, rect1);
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
-  DrawRect(context, content, kForegroundType, rect3);
-  GenerateRasterInvalidations();
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    DrawRect(context, content, kBackgroundType, rect1);
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+    DrawRect(context, content, kForegroundType, rect3);
+  }
 
   invalidator_.SetTracksRasterInvalidations(true);
-  InitRootChunk();
-  // Draw again with nothing invalidated.
-  DrawRect(context, content, kBackgroundType, rect1);
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
-  DrawRect(context, content, kForegroundType, rect3);
+  {
+    RasterInvalidationCycleScope cycle_scope(GetPaintController(),
+                                             invalidator_);
+    InitRootChunk();
+    // Draw again with nothing invalidated.
+    DrawRect(context, content, kBackgroundType, rect1);
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+    DrawRect(context, content, kForegroundType, rect3);
+  }
 
   EXPECT_THAT(
-      GenerateRasterInvalidations(),
+      GetRasterInvalidations(),
       UnorderedElementsAre(RasterInvalidationInfo{
           &content, "content", UnionRect(rect1, UnionRect(rect2, rect3)),
           PaintInvalidationReason::kUncacheable}));
diff --git a/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc b/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc
index 42eb5e04..f5295542 100644
--- a/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc
@@ -22,9 +22,12 @@
 TEST_F(DrawingRecorderTest, Nothing) {
   FakeDisplayItemClient client;
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
-  DrawNothing(context, client, kForegroundType);
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawNothing(context, client, kForegroundType);
+    GetPaintController().CommitNewDisplayItems();
+  }
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kForegroundType)));
   EXPECT_FALSE(
@@ -35,9 +38,12 @@
 TEST_F(DrawingRecorderTest, Rect) {
   FakeDisplayItemClient client;
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
-  DrawRect(context, client, kForegroundType, kBounds);
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, client, kForegroundType, kBounds);
+    GetPaintController().CommitNewDisplayItems();
+  }
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kForegroundType)));
 }
@@ -45,22 +51,28 @@
 TEST_F(DrawingRecorderTest, Cached) {
   FakeDisplayItemClient client;
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
-  DrawNothing(context, client, kBackgroundType);
-  DrawRect(context, client, kForegroundType, kBounds);
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawNothing(context, client, kBackgroundType);
+    DrawRect(context, client, kForegroundType, kBounds);
+    GetPaintController().CommitNewDisplayItems();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kBackgroundType),
                           IsSameId(&client, kForegroundType)));
 
-  InitRootChunk();
-  DrawNothing(context, client, kBackgroundType);
-  DrawRect(context, client, kForegroundType, kBounds);
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawNothing(context, client, kBackgroundType);
+    DrawRect(context, client, kForegroundType, kBounds);
 
-  EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(2u, NumCachedNewItems());
 
-  CommitAndFinishCycle();
+    GetPaintController().CommitNewDisplayItems();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kBackgroundType),
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
index 7c40c44..43d80b3 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
@@ -34,6 +34,26 @@
 }
 #endif
 
+void PaintChunker::StartMarkingClientsForValidation(
+    Vector<const DisplayItemClient*>& clients_to_validate) {
+#if DCHECK_IS_ON()
+  DCHECK(IsInInitialState());
+#endif
+  DCHECK(!clients_to_validate_);
+  clients_to_validate_ = &clients_to_validate;
+}
+
+void PaintChunker::MarkClientForValidation(const DisplayItemClient& client) {
+  if (clients_to_validate_ && !client.IsMarkedForValidation()) {
+    clients_to_validate_->push_back(&client);
+    client.MarkForValidation();
+  }
+}
+
+void PaintChunker::StopMarkingClientsForValidation() {
+  clients_to_validate_ = nullptr;
+}
+
 void PaintChunker::UpdateCurrentPaintChunkProperties(
     const PaintChunk::Id* chunk_id,
     const PropertyTreeStateOrAlias& properties) {
@@ -55,6 +75,10 @@
   wtf_size_t next_chunk_begin_index =
       chunks_->IsEmpty() ? 0 : chunks_->back().end_index;
   chunks_->emplace_back(next_chunk_begin_index, std::move(chunk));
+  // This chunk was copied from the cache, so it should already be valid; hence
+  // we don't call MarkClientForValidation().
+  DCHECK(!chunks_->back().id.client.IsCacheable() ||
+         chunks_->back().id.client.IsValid());
 }
 
 bool PaintChunker::EnsureCurrentChunk(const PaintChunk::Id& id) {
@@ -73,6 +97,7 @@
       next_chunk_id_.emplace(id);
     FinalizeLastChunkProperties();
     wtf_size_t begin = chunks_->IsEmpty() ? 0 : chunks_->back().end_index;
+    MarkClientForValidation(next_chunk_id_->client);
     chunks_->emplace_back(begin, begin, *next_chunk_id_, current_properties_,
                           current_effectively_invisible_);
     next_chunk_id_ = absl::nullopt;
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
index 7a18b74..9373392 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
@@ -37,6 +37,11 @@
   bool IsInInitialState() const;
 #endif
 
+  void StartMarkingClientsForValidation(
+      Vector<const DisplayItemClient*>& clients_to_validate);
+  void MarkClientForValidation(const DisplayItemClient& client);
+  void StopMarkingClientsForValidation();
+
   const PropertyTreeStateOrAlias& CurrentPaintChunkProperties() const {
     return current_properties_;
   }
@@ -101,6 +106,7 @@
   void FinalizeLastChunkProperties();
 
   Vector<PaintChunk>* chunks_ = nullptr;
+  Vector<const DisplayItemClient*>* clients_to_validate_ = nullptr;
 
   // The id specified by UpdateCurrentPaintChunkProperties(). If it is not
   // nullopt, we will use it as the id of the next new chunk. Otherwise we will
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
index 45ebb7c..c2b4895 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -231,6 +231,8 @@
     return false;
   }
 
+  // This subsequence was copied from the cache, so client must already be
+  // valid, hence we don't call MarkClientForValidation(client).
   AppendSubsequenceByMoving(client, subsequence_index,
                             markers.start_chunk_index, markers.end_chunk_index);
   return true;
@@ -327,6 +329,13 @@
     under_invalidation_checker_->CheckNewItem();
 }
 
+void PaintController::MarkClientForValidation(const DisplayItemClient& client) {
+  if (clients_to_validate_ && !client.IsMarkedForValidation()) {
+    clients_to_validate_->push_back(&client);
+    client.MarkForValidation();
+  }
+}
+
 void PaintController::ProcessNewItem(DisplayItem& display_item) {
   if (IsSkippingCache() && usage_ == kMultiplePaints) {
     display_item.Client().Invalidate(PaintInvalidationReason::kUncacheable);
@@ -526,6 +535,8 @@
   for (auto& item : new_display_item_list.ItemsInRange(
            new_item_start_index, new_display_item_list.size())) {
     DCHECK(!item.IsTombstone());
+    // This item was copied from the cache, so client must already be valid,
+    // hence we don't call MarkClientForValidation(client).
     item.SetPaintInvalidationReason(skip_cache || !item.IsCacheable()
                                         ? PaintInvalidationReason::kUncacheable
                                         : PaintInvalidationReason::kNone);
@@ -615,50 +626,40 @@
 #endif
 }
 
+PaintController::CycleScope::~CycleScope() {
+  for (const auto* client : clients_to_validate_) {
+    if (client->IsCacheable())
+      client->Validate();
+  }
+  for (auto* controller : controllers_)
+    controller->FinishCycle();
+}
+
+void PaintController::StartCycle(
+    Vector<const DisplayItemClient*>& clients_to_validate) {
+  // StartCycle() can only be called before the controller has painted anything.
+  DCHECK(new_paint_artifact_);
+  DCHECK(new_paint_artifact_->IsEmpty());
+  DCHECK(!clients_to_validate_);
+  if (usage_ == kTransient)
+    return;
+  clients_to_validate_ = &clients_to_validate;
+  paint_chunker_.StartMarkingClientsForValidation(clients_to_validate);
+  ReserveCapacity();
+}
+
 void PaintController::FinishCycle() {
+  DCHECK(usage_ == kTransient || clients_to_validate_);
+  clients_to_validate_ = nullptr;
+  paint_chunker_.StopMarkingClientsForValidation();
   if (usage_ == kTransient || !committed_)
     return;
 
   CheckNoNewPaint();
   committed_ = false;
 
-  // Validate display item clients that have validly cached subsequence or
-  // display items in this PaintController.
-  for (auto& item : current_subsequences_.tree) {
-    if (item.is_moved_from_cached_subsequence) {
-      // We don't need to validate the client of a cached subsequence, because
-      // it should be already valid. See http://crbug.com/1050090 for more
-      // details.
-      DCHECK(!item.client->IsCacheable() || ClientCacheIsValid(*item.client));
-      continue;
-    }
-    if (item.client->IsCacheable())
-      item.client->Validate();
-  }
-  for (wtf_size_t i = 0; i < current_paint_artifact_->PaintChunks().size();
-       i++) {
-    auto& chunk = current_paint_artifact_->PaintChunks()[i];
+  for (auto& chunk : current_paint_artifact_->PaintChunks())
     chunk.client_is_just_created = false;
-    const auto& client = chunk.id.client;
-    if (chunk.is_moved_from_cached_subsequence) {
-      // We don't need to validate the clients of paint chunks and display
-      // items that are moved from a cached subsequence, because they should be
-      // already valid. See http://crbug.com/1050090 for more details.
-#if DCHECK_IS_ON()
-      DCHECK(!chunk.is_cacheable || ClientCacheIsValid(client));
-      for (const auto& item : current_paint_artifact_->DisplayItemsInChunk(i))
-        DCHECK(!item.IsCacheable() || ClientCacheIsValid(item.Client()));
-#endif
-      continue;
-    }
-    if (client.IsCacheable())
-      client.Validate();
-
-    for (const auto& item : current_paint_artifact_->DisplayItemsInChunk(i)) {
-      if (item.Client().IsCacheable())
-        item.Client().Validate();
-    }
-  }
 
 #if DCHECK_IS_ON()
   if (VLOG_IS_ON(1)) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
index 5adfdf91..8a2b98c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -82,15 +82,32 @@
   PaintController& operator=(const PaintController&) = delete;
   ~PaintController();
 
-  // Called before painting to optimize memory allocation by reserving space in
-  // |new_paint_artifact_| and |new_subsequences_| based on the size of the
-  // previous ones (|current_paint_artifact_| and |current_subsequences_|).
-  void ReserveCapacity();
-
 #if DCHECK_IS_ON()
   Usage GetUsage() const { return usage_; }
 #endif
 
+  class PLATFORM_EXPORT CycleScope {
+    STACK_ALLOCATED();
+
+   public:
+    CycleScope() = default;
+    explicit CycleScope(PaintController& controller) {
+      AddController(controller);
+    }
+    void AddController(PaintController& controller) {
+      controller.StartCycle(clients_to_validate_);
+      controllers_.push_back(&controller);
+    }
+    ~CycleScope();
+
+   protected:
+    Vector<PaintController*> controllers_;
+
+   private:
+    Vector<const DisplayItemClient*> clients_to_validate_;
+  };
+  friend class CycleScope;
+
   // These methods are called during painting.
 
   // Provide a new set of paint chunk properties to apply to recorded display
@@ -144,12 +161,15 @@
     return new_paint_artifact_->PaintChunks().back().bounds;
   }
 
+  void MarkClientForValidation(const DisplayItemClient& client);
+
   template <typename DisplayItemClass, typename... Args>
-  void CreateAndAppend(Args&&... args) {
+  void CreateAndAppend(const DisplayItemClient& client, Args&&... args) {
+    MarkClientForValidation(client);
     DisplayItemClass& display_item =
         new_paint_artifact_->GetDisplayItemList()
             .AllocateAndConstruct<DisplayItemClass>(
-                std::forward<Args>(args)...);
+                client, std::forward<Args>(args)...);
     display_item.SetFragment(current_fragment_);
     ProcessNewItem(display_item);
   }
@@ -189,14 +209,6 @@
   // artifact with the new paintings.
   void CommitNewDisplayItems();
 
-  // Called when the caller finishes updating a full document life cycle.
-  // The PaintController will cleanup data that will no longer be used for the
-  // next cycle, and update status to be ready for the next cycle.
-  // It updates caching status of DisplayItemClients, so if there are
-  // DisplayItemClients painting on multiple PaintControllers, we should call
-  // there FinishCycle() at the same time to ensure consistent caching status.
-  void FinishCycle();
-
   // Returns the approximate memory usage owned by this PaintController.
   size_t ApproximateUnsharedMemoryUsage() const;
 
@@ -291,6 +303,22 @@
   friend class PaintUnderInvalidationChecker;
   friend class GraphicsLayer;  // Temporary for ClientCacheIsValid().
 
+  // Called before painting to optimize memory allocation by reserving space in
+  // |new_paint_artifact_| and |new_subsequences_| based on the size of the
+  // previous ones (|current_paint_artifact_| and |current_subsequences_|).
+  void ReserveCapacity();
+
+  // Called at the beginning of a paint cycle, as defined by CycleScope.
+  void StartCycle(Vector<const DisplayItemClient*>& clients_to_validate);
+
+  // Called at the end of a paint cycle, as defined by CycleScope.
+  // The PaintController will cleanup data that will no longer be used for the
+  // next cycle, and update status to be ready for the next cycle.
+  // It updates caching status of DisplayItemClients, so if there are
+  // DisplayItemClients painting on multiple PaintControllers, we should call
+  // there FinishCycle() at the same time to ensure consistent caching status.
+  void FinishCycle();
+
   // True if all display items associated with the client are validly cached.
   // However, the current algorithm allows the following situations even if
   // ClientCacheIsValid() is true for a client during painting:
@@ -375,6 +403,7 @@
   // CommitNewDisplayItems().
   scoped_refptr<PaintArtifact> new_paint_artifact_;
   PaintChunker paint_chunker_;
+  Vector<const DisplayItemClient*>* clients_to_validate_ = nullptr;
 
   bool cache_is_all_invalid_ = true;
   bool committed_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
index 4049044..b64b32d 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
 
+#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
@@ -25,6 +26,16 @@
                             public PaintControllerTestBase {
 };
 
+class CommitCycleScope : public PaintController::CycleScope {
+ public:
+  explicit CommitCycleScope(PaintController& controller)
+      : PaintController::CycleScope(controller) {}
+  ~CommitCycleScope() {
+    for (auto* controller : controllers_)
+      controller->CommitNewDisplayItems();
+  }
+};
+
 #define EXPECT_DEFAULT_ROOT_CHUNK(size)                               \
   EXPECT_THAT(GetPaintController().PaintChunks(),                     \
               ElementsAre(IsPaintChunk(0, size, DefaultRootChunkId(), \
@@ -37,14 +48,15 @@
                     kCompositeAfterPaint,
                     kUnderInvalidationChecking,
                     kCompositeAfterPaint | kUnderInvalidationChecking));
-
 TEST_P(PaintControllerTest, NestedRecorders) {
   GraphicsContext context(GetPaintController());
   FakeDisplayItemClient client("client");
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, client, kBackgroundType, IntRect(100, 100, 200, 200));
-  CommitAndFinishCycle();
+    DrawRect(context, client, kBackgroundType, IntRect(100, 100, 200, 200));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kBackgroundType)));
@@ -55,16 +67,17 @@
   FakeDisplayItemClient first("first");
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
 
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
-
-  CommitAndFinishCycle();
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -72,19 +85,20 @@
                           IsSameId(&first, kForegroundType)));
   EXPECT_DEFAULT_ROOT_CHUNK(3);
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 300, 300));
 
-  EXPECT_EQ(2u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(1u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(1u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -97,15 +111,17 @@
   FakeDisplayItemClient second("second");
   FakeDisplayItemClient unaffected("unaffected");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
-  CommitAndFinishCycle();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+    DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -115,24 +131,25 @@
                           IsSameId(&unaffected, kBackgroundType),
                           IsSameId(&unaffected, kForegroundType)));
 
-  InitRootChunk();
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+    DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
 
-  EXPECT_EQ(6u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(6u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(2u, NumIndexedItems());  // first
-  EXPECT_EQ(5u,
-            NumSequentialMatches());  // second, first foreground, unaffected
-  EXPECT_EQ(1u, NumOutOfOrderMatches());  // first
+    EXPECT_EQ(2u, NumIndexedItems());  // first
+    EXPECT_EQ(5u,
+              NumSequentialMatches());  // second, first foreground, unaffected
+    EXPECT_EQ(1u, NumOutOfOrderMatches());  // first
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&second, kBackgroundType),
@@ -149,15 +166,17 @@
   FakeDisplayItemClient second("second");
   FakeDisplayItemClient unaffected("unaffected");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
-  CommitAndFinishCycle();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+    DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -167,24 +186,25 @@
                           IsSameId(&unaffected, kBackgroundType),
                           IsSameId(&unaffected, kForegroundType)));
 
-  InitRootChunk();
-  first.Invalidate();
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
-  DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    first.Invalidate();
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, unaffected, kBackgroundType, IntRect(300, 300, 10, 10));
+    DrawRect(context, unaffected, kForegroundType, IntRect(300, 300, 10, 10));
 
-  EXPECT_EQ(4u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(4u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(2u, NumIndexedItems());
-  EXPECT_EQ(4u, NumSequentialMatches());  // second, unaffected
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(2u, NumIndexedItems());
+    EXPECT_EQ(4u, NumSequentialMatches());  // second, unaffected
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&second, kBackgroundType),
@@ -201,31 +221,34 @@
   FakeDisplayItemClient second("second");
   FakeDisplayItemClient third("third");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  CommitAndFinishCycle();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
                           IsSameId(&second, kBackgroundType)));
 
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, third, kBackgroundType, IntRect(125, 100, 200, 50));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, third, kBackgroundType, IntRect(125, 100, 200, 50));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
 
-  EXPECT_EQ(2u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(0u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());  // first, second
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(0u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());  // first, second
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -239,15 +262,17 @@
   FakeDisplayItemClient second("second");
   FakeDisplayItemClient third("third");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, third, kBackgroundType, IntRect(300, 100, 50, 50));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, third, kForegroundType, IntRect(300, 100, 50, 50));
-  CommitAndFinishCycle();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, third, kBackgroundType, IntRect(300, 100, 50, 50));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, third, kForegroundType, IntRect(300, 100, 50, 50));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -257,25 +282,26 @@
                           IsSameId(&second, kForegroundType),
                           IsSameId(&third, kForegroundType)));
 
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, third, kBackgroundType, IntRect(300, 100, 50, 50));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, third, kForegroundType, IntRect(300, 100, 50, 50));
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, third, kBackgroundType, IntRect(300, 100, 50, 50));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, second, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, third, kForegroundType, IntRect(300, 100, 50, 50));
 
-  EXPECT_EQ(4u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(4u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(2u, NumIndexedItems());
-  EXPECT_EQ(4u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(2u, NumIndexedItems());
+    EXPECT_EQ(4u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -291,27 +317,31 @@
   FakeDisplayItemClient first("first");
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
-  DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
-  CommitAndFinishCycle();
+    DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
+    DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&second, kBackgroundType),
                           IsSameId(&second, kForegroundType)));
 
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  first.Invalidate();
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
-  DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+    first.Invalidate();
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
+    DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -320,19 +350,20 @@
                           IsSameId(&second, kForegroundType)));
   EXPECT_DEFAULT_ROOT_CHUNK(4);
 
-  InitRootChunk();
-  DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
-  DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, second, kBackgroundType, IntRect(150, 250, 100, 100));
+    DrawRect(context, second, kForegroundType, IntRect(150, 250, 100, 100));
 
-  EXPECT_EQ(2u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(2u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(2u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&second, kBackgroundType),
@@ -344,27 +375,31 @@
   FakeDisplayItemClient first("first");
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
-  CommitAndFinishCycle();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
                           IsSameId(&first, kForegroundType)));
 
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  first.Invalidate();
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(150, 150, 100, 100));
-  DrawRect(context, first, kForegroundType, IntRect(150, 150, 100, 100));
-  DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
-  DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+    first.Invalidate();
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(150, 150, 100, 100));
+    DrawRect(context, first, kForegroundType, IntRect(150, 150, 100, 100));
+    DrawRect(context, second, kBackgroundType, IntRect(200, 200, 50, 50));
+    DrawRect(context, second, kForegroundType, IntRect(200, 200, 50, 50));
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -373,14 +408,16 @@
                           IsSameId(&second, kForegroundType)));
   EXPECT_DEFAULT_ROOT_CHUNK(4);
 
-  InitRootChunk();
-  first.Invalidate();
-  second.Invalidate();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    first.Invalidate();
+    second.Invalidate();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, first, kForegroundType, IntRect(100, 100, 150, 150));
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -392,11 +429,13 @@
   FakeDisplayItemClient first("first");
   FakeDisplayItemClient second("second");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 150, 150));
-  CommitAndFinishCycle();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 150, 150));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -414,10 +453,12 @@
   EXPECT_FALSE(ClientCacheIsValid(first));
   EXPECT_TRUE(ClientCacheIsValid(second));
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 150, 150));
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 150, 150));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 150, 150));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
@@ -447,17 +488,19 @@
   FakeDisplayItemClient container2("container2");
   FakeDisplayItemClient content2("content2");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  CommitAndFinishCycle();
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -469,19 +512,21 @@
                           IsSameId(&content2, kForegroundType),
                           IsSameId(&container2, kForegroundType)));
 
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  // Simulate the situation when |container1| gets a z-index that is greater
-  // than that of |container2|.
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  CommitAndFinishCycle();
+    // Simulate the situation when |container1| gets a z-index that is greater
+    // than that of |container2|.
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container2, kBackgroundType),
@@ -501,17 +546,19 @@
   FakeDisplayItemClient container2("container2");
   FakeDisplayItemClient content2("content2");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  CommitAndFinishCycle();
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -523,20 +570,22 @@
                           IsSameId(&content2, kForegroundType),
                           IsSameId(&container2, kForegroundType)));
 
-  InitRootChunk();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  // Simulate the situation when |container1| gets a z-index that is greater
-  // than that of |container2|, and |container1| is invalidated.
-  container1.Invalidate();
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  CommitAndFinishCycle();
+    // Simulate the situation when |container1| gets a z-index that is greater
+    // than that of |container2|, and |container1| is invalidated.
+    container1.Invalidate();
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container2, kBackgroundType),
@@ -554,30 +603,34 @@
   if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
     return;
 
-  GraphicsContext context(GetPaintController());
-
   FakeDisplayItemClient root("root");
   auto root_properties = DefaultPaintChunkProperties();
   PaintChunk::Id root_id(root, DisplayItem::kCaret);
-  GetPaintController().UpdateCurrentPaintChunkProperties(&root_id,
-                                                         root_properties);
-  DrawRect(context, root, kBackgroundType, IntRect(100, 100, 100, 100));
-
   FakeDisplayItemClient container("container");
   auto container_properties = DefaultPaintChunkProperties();
   PaintChunk::Id container_id(container, DisplayItem::kCaret);
+  GraphicsContext context(GetPaintController());
+
   {
-    SubsequenceRecorder r(context, container);
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container_id, container_properties);
-    DrawRect(context, container, kBackgroundType, IntRect(100, 100, 100, 100));
-    DrawRect(context, container, kForegroundType, IntRect(100, 100, 100, 100));
+    CommitCycleScope cycle_scope(GetPaintController());
+
+    GetPaintController().UpdateCurrentPaintChunkProperties(&root_id,
+                                                           root_properties);
+    DrawRect(context, root, kBackgroundType, IntRect(100, 100, 100, 100));
+
+    {
+      SubsequenceRecorder r(context, container);
+      GetPaintController().UpdateCurrentPaintChunkProperties(
+          &container_id, container_properties);
+      DrawRect(context, container, kBackgroundType,
+               IntRect(100, 100, 100, 100));
+      DrawRect(context, container, kForegroundType,
+               IntRect(100, 100, 100, 100));
+    }
+
+    DrawRect(context, root, kForegroundType, IntRect(100, 100, 100, 100));
   }
 
-  DrawRect(context, root, kForegroundType, IntRect(100, 100, 100, 100));
-
-  CommitAndFinishCycle();
-
   // Even though the paint properties match, |container| should receive its
   // own PaintChunk because it created a subsequence.
   EXPECT_THAT(
@@ -587,12 +640,14 @@
                   IsPaintChunk(3, 4, PaintChunk::Id(root, kForegroundType),
                                root_properties)));
 
-  GetPaintController().UpdateCurrentPaintChunkProperties(&root_id,
-                                                         root_properties);
-  DrawRect(context, root, kBackgroundType, IntRect(100, 100, 100, 100));
-  EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(container));
-  DrawRect(context, root, kForegroundType, IntRect(100, 100, 100, 100));
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    GetPaintController().UpdateCurrentPaintChunkProperties(&root_id,
+                                                           root_properties);
+    DrawRect(context, root, kBackgroundType, IntRect(100, 100, 100, 100));
+    EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(container));
+    DrawRect(context, root, kForegroundType, IntRect(100, 100, 100, 100));
+  }
 
   // |container| should still receive its own PaintChunk because it is a cached
   // subsequence.
@@ -622,26 +677,33 @@
   container2_properties.SetEffect(*container2_effect);
 
   {
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container1_id, container1_properties);
+    CommitCycleScope cycle_scope(GetPaintController());
 
-    SubsequenceRecorder r(context, container1);
-    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  }
-  {
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container2_id, container2_properties);
+    {
+      GetPaintController().UpdateCurrentPaintChunkProperties(
+          &container1_id, container1_properties);
 
-    SubsequenceRecorder r(context, container2);
-    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+      SubsequenceRecorder r(context, container1);
+      DrawRect(context, container1, kBackgroundType,
+               IntRect(100, 100, 100, 100));
+      DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+      DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+      DrawRect(context, container1, kForegroundType,
+               IntRect(100, 100, 100, 100));
+    }
+    {
+      GetPaintController().UpdateCurrentPaintChunkProperties(
+          &container2_id, container2_properties);
+
+      SubsequenceRecorder r(context, container2);
+      DrawRect(context, container2, kBackgroundType,
+               IntRect(100, 200, 100, 100));
+      DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+      DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+      DrawRect(context, container2, kForegroundType,
+               IntRect(100, 200, 100, 100));
+    }
   }
-  CommitAndFinishCycle();
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -664,56 +726,61 @@
       ElementsAre(IsPaintChunk(0, 4, container1_id, container1_properties),
                   IsPaintChunk(4, 8, container2_id, container2_properties)));
 
-  // Simulate the situation when |container1| gets a z-index that is greater
-  // than that of |container2|.
-  if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
-    // When under-invalidation-checking is enabled,
-    // UseCachedSubsequenceIfPossible is forced off, and the client is expected
-    // to create the same painting as in the previous paint.
-    EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container2));
-    {
-      GetPaintController().UpdateCurrentPaintChunkProperties(
-          &container2_id, container2_properties);
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    // Simulate the situation when |container1| gets a z-index that is greater
+    // than that of |container2|.
+    if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+      // When under-invalidation-checking is enabled,
+      // UseCachedSubsequenceIfPossible is forced off, and the client is
+      // expected to create the same painting as in the previous paint.
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container2));
+      {
+        GetPaintController().UpdateCurrentPaintChunkProperties(
+            &container2_id, container2_properties);
 
-      SubsequenceRecorder r(context, container2);
-      DrawRect(context, container2, kBackgroundType,
-               IntRect(100, 200, 100, 100));
-      DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-      DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-      DrawRect(context, container2, kForegroundType,
-               IntRect(100, 200, 100, 100));
+        SubsequenceRecorder r(context, container2);
+        DrawRect(context, container2, kBackgroundType,
+                 IntRect(100, 200, 100, 100));
+        DrawRect(context, content2, kBackgroundType,
+                 IntRect(100, 200, 50, 200));
+        DrawRect(context, content2, kForegroundType,
+                 IntRect(100, 200, 50, 200));
+        DrawRect(context, container2, kForegroundType,
+                 IntRect(100, 200, 100, 100));
+      }
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container1));
+      {
+        GetPaintController().UpdateCurrentPaintChunkProperties(
+            &container1_id, container1_properties);
+
+        SubsequenceRecorder r(context, container1);
+        DrawRect(context, container1, kBackgroundType,
+                 IntRect(100, 100, 100, 100));
+        DrawRect(context, content1, kBackgroundType,
+                 IntRect(100, 100, 50, 200));
+        DrawRect(context, content1, kForegroundType,
+                 IntRect(100, 100, 50, 200));
+        DrawRect(context, container1, kForegroundType,
+                 IntRect(100, 100, 100, 100));
+      }
+    } else {
+      EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container2));
+      EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container1));
     }
-    EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container1));
-    {
-      GetPaintController().UpdateCurrentPaintChunkProperties(
-          &container1_id, container1_properties);
 
-      SubsequenceRecorder r(context, container1);
-      DrawRect(context, container1, kBackgroundType,
-               IntRect(100, 100, 100, 100));
-      DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-      DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-      DrawRect(context, container1, kForegroundType,
-               IntRect(100, 100, 100, 100));
-    }
-  } else {
-    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container2));
-    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container1));
-  }
-
-  EXPECT_EQ(8u, NumCachedNewItems());
-  EXPECT_EQ(2u, NumCachedNewSubsequences());
+    EXPECT_EQ(8u, NumCachedNewItems());
+    EXPECT_EQ(2u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(0u, NumIndexedItems());
-  EXPECT_EQ(0u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(0u, NumIndexedItems());
+    EXPECT_EQ(0u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container2, kBackgroundType),
@@ -746,18 +813,22 @@
   PaintChunk::Id container2_id(container2, kBackgroundType);
   PaintChunk::Id content2_id(content2, kBackgroundType);
 
-  InitRootChunk();
-
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
   {
-    SubsequenceRecorder r(context, container2);
-    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-    DrawRect(context, container2, kForegroundType, IntRect(100, 200, 100, 100));
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    {
+      SubsequenceRecorder r(context, container2);
+      DrawRect(context, container2, kBackgroundType,
+               IntRect(100, 200, 100, 100));
+      DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+      DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
+      DrawRect(context, container2, kForegroundType,
+               IntRect(100, 200, 100, 100));
+    }
+    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
   }
-  DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  CommitAndFinishCycle();
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&content1, kBackgroundType),
@@ -783,42 +854,45 @@
 
   // Simulate the situation when |container2| gets a z-index that is smaller
   // than that of |content1|.
-  InitRootChunk();
-  if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
-    // When under-invalidation-checking is enabled,
-    // UseCachedSubsequenceIfPossible is forced off, and the client is expected
-    // to create the same painting as in the previous paint.
-    EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container2));
-    {
-      SubsequenceRecorder r(context, container2);
-      DrawRect(context, container2, kBackgroundType,
-               IntRect(100, 200, 100, 100));
-      DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-      DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-      DrawRect(context, container2, kForegroundType,
-               IntRect(100, 200, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+      // When under-invalidation-checking is enabled,
+      // UseCachedSubsequenceIfPossible is forced off, and the client is
+      // expected to create the same painting as in the previous paint.
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container2));
+      {
+        SubsequenceRecorder r(context, container2);
+        DrawRect(context, container2, kBackgroundType,
+                 IntRect(100, 200, 100, 100));
+        DrawRect(context, content2, kBackgroundType,
+                 IntRect(100, 200, 50, 200));
+        DrawRect(context, content2, kForegroundType,
+                 IntRect(100, 200, 50, 200));
+        DrawRect(context, container2, kForegroundType,
+                 IntRect(100, 200, 100, 100));
+      }
+      DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+      DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+    } else {
+      EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container2));
+      EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
+                                                              kBackgroundType));
+      EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
+                                                              kForegroundType));
     }
-    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-    DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-  } else {
-    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container2));
-    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
-                                                            kBackgroundType));
-    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
-                                                            kForegroundType));
-  }
 
-  EXPECT_EQ(6u, NumCachedNewItems());
-  EXPECT_EQ(1u, NumCachedNewSubsequences());
+    EXPECT_EQ(6u, NumCachedNewItems());
+    EXPECT_EQ(1u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(0u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(0u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container2, kBackgroundType),
@@ -854,16 +928,18 @@
   PaintChunk::Id content3_id(content3, kBackgroundType);
   IntRect rect(100, 100, 50, 200);
 
-  InitRootChunk();
-
-  DrawRect(context, content1a, kBackgroundType, rect);
-  DrawRect(context, content1b, kBackgroundType, rect);
   {
-    SubsequenceRecorder r(context, container2);
-    DrawRect(context, container2, kBackgroundType, rect);
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+
+    DrawRect(context, content1a, kBackgroundType, rect);
+    DrawRect(context, content1b, kBackgroundType, rect);
+    {
+      SubsequenceRecorder r(context, container2);
+      DrawRect(context, container2, kBackgroundType, rect);
+    }
+    DrawRect(context, content3, kBackgroundType, rect);
   }
-  DrawRect(context, content3, kBackgroundType, rect);
-  CommitAndFinishCycle();
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&content1a, kBackgroundType),
@@ -875,43 +951,44 @@
   // Subsequence(container1): container1, content1b(cached), content1a(cached).
   // Subsequence(container2): cached
   // Subsequence(contaienr3): container3, content3
-  InitRootChunk();
-  if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
-    EXPECT_FALSE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1b,
-                                                             kBackgroundType));
-    DrawRect(context, content1b, kBackgroundType, rect);
-    EXPECT_FALSE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1a,
-                                                             kBackgroundType));
-    DrawRect(context, content1a, kBackgroundType, rect);
-    {
-      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+      EXPECT_FALSE(DrawingRecorder::UseCachedDrawingIfPossible(
+          context, content1b, kBackgroundType));
+      DrawRect(context, content1b, kBackgroundType, rect);
+      EXPECT_FALSE(DrawingRecorder::UseCachedDrawingIfPossible(
+          context, content1a, kBackgroundType));
+      DrawRect(context, content1a, kBackgroundType, rect);
+      {
+        EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, container2));
+        SubsequenceRecorder r(context, container2);
+        DrawRect(context, container2, kBackgroundType, rect);
+      }
+      EXPECT_FALSE(DrawingRecorder::UseCachedDrawingIfPossible(
+          context, content3, kBackgroundType));
+      DrawRect(context, content3, kBackgroundType, rect);
+    } else {
+      EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(
+          context, content1b, kBackgroundType));
+      EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(
+          context, content1a, kBackgroundType));
+      EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
           context, container2));
-      SubsequenceRecorder r(context, container2);
-      DrawRect(context, container2, kBackgroundType, rect);
+      EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content3,
+                                                              kBackgroundType));
     }
-    EXPECT_FALSE(DrawingRecorder::UseCachedDrawingIfPossible(context, content3,
-                                                             kBackgroundType));
-    DrawRect(context, content3, kBackgroundType, rect);
-  } else {
-    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1b,
-                                                            kBackgroundType));
-    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1a,
-                                                            kBackgroundType));
-    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container2));
-    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content3,
-                                                            kBackgroundType));
-  }
 
-  EXPECT_EQ(4u, NumCachedNewItems());
-  EXPECT_EQ(1u, NumCachedNewSubsequences());
+    EXPECT_EQ(4u, NumCachedNewItems());
+    EXPECT_EQ(1u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(1u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());
-  EXPECT_EQ(1u, NumOutOfOrderMatches());
+    EXPECT_EQ(1u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());
+    EXPECT_EQ(1u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&content1b, kBackgroundType),
@@ -926,7 +1003,6 @@
   constexpr wtf_size_t kFragmentCount = 3;
   FakeDisplayItemClient container("container");
 
-  // The first paint.
   auto paint_container = [this, &context, &container]() {
     SubsequenceRecorder r(context, container);
     for (wtf_size_t i = 0; i < kFragmentCount; ++i) {
@@ -938,7 +1014,10 @@
                IntRect(100, 100, 100, 100));
     }
   };
+
+  // The first paint.
   {
+    CommitCycleScope cycle_scope(GetPaintController());
     ScopedPaintChunkProperties root_chunk_properties(
         GetPaintController(), DefaultPaintChunkProperties(), root,
         kBackgroundType);
@@ -946,7 +1025,6 @@
     paint_container();
     DrawRect(context, root, kForegroundType, IntRect(100, 100, 100, 100));
   }
-  CommitAndFinishCycle();
 
   auto check_paint_results = [this, &root, &container]() {
     EXPECT_THAT(
@@ -966,6 +1044,7 @@
 
   // The second paint.
   {
+    CommitCycleScope cycle_scope(GetPaintController());
     ScopedPaintChunkProperties root_chunk_properties(
         GetPaintController(), DefaultPaintChunkProperties(), root,
         kBackgroundType);
@@ -981,7 +1060,6 @@
     }
     DrawRect(context, root, kForegroundType, IntRect(100, 100, 100, 100));
   }
-  CommitAndFinishCycle();
 
   // The second paint should produce the exactly same results.
   check_paint_results();
@@ -1004,15 +1082,17 @@
   auto container2_properties = DefaultPaintChunkProperties();
   container2_properties.SetEffect(*container2_effect);
 
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id,
-                                                         container1_properties);
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id,
-                                                         container2_properties);
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container1_id, container1_properties);
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container2_id, container2_properties);
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -1026,24 +1106,25 @@
                   IsPaintChunk(2, 4, container2_id, container2_properties)));
 
   // Move content2 into container1, without invalidation.
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id,
-                                                         container1_properties);
-  DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-  DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
-  GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id,
-                                                         container2_properties);
-  DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container1_id, container1_properties);
+    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
+    DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+    GetPaintController().UpdateCurrentPaintChunkProperties(
+        &container2_id, container2_properties);
+    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
 
-  EXPECT_EQ(4u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(4u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(1u, NumIndexedItems());
-  EXPECT_EQ(3u, NumSequentialMatches());
-  EXPECT_EQ(1u, NumOutOfOrderMatches());
+    EXPECT_EQ(1u, NumIndexedItems());
+    EXPECT_EQ(3u, NumSequentialMatches());
+    EXPECT_EQ(1u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -1069,21 +1150,23 @@
   const DisplayItem::Type kType4 =
       static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 3);
 
-  InitRootChunk();
-  DrawRect(context, client, kType1, IntRect(100, 100, 100, 100));
-  DrawRect(context, client, kType2, IntRect(100, 100, 50, 200));
-  DrawRect(context, client, kType3, IntRect(100, 100, 50, 200));
-  DrawRect(context, client, kType4, IntRect(100, 100, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, client, kType1, IntRect(100, 100, 100, 100));
+    DrawRect(context, client, kType2, IntRect(100, 100, 50, 200));
+    DrawRect(context, client, kType3, IntRect(100, 100, 50, 200));
+    DrawRect(context, client, kType4, IntRect(100, 100, 100, 100));
+  }
 
-  CommitAndFinishCycle();
-
-  InitRootChunk();
-  DrawRect(context, client, kType2, IntRect(100, 100, 50, 200));
-  DrawRect(context, client, kType3, IntRect(100, 100, 50, 200));
-  DrawRect(context, client, kType1, IntRect(100, 100, 100, 100));
-  DrawRect(context, client, kType4, IntRect(100, 100, 100, 100));
-
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, client, kType2, IntRect(100, 100, 50, 200));
+    DrawRect(context, client, kType3, IntRect(100, 100, 50, 200));
+    DrawRect(context, client, kType1, IntRect(100, 100, 100, 100));
+    DrawRect(context, client, kType4, IntRect(100, 100, 100, 100));
+  }
 }
 
 TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) {
@@ -1117,35 +1200,43 @@
   content2_properties.SetEffect(*content2_effect);
 
   {
-    SubsequenceRecorder r(context, container1);
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container1_background_id, container1_background_properties);
-    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
+    CommitCycleScope cycle_scope(GetPaintController());
+    {
+      SubsequenceRecorder r(context, container1);
+      GetPaintController().UpdateCurrentPaintChunkProperties(
+          &container1_background_id, container1_background_properties);
+      DrawRect(context, container1, kBackgroundType,
+               IntRect(100, 100, 100, 100));
 
-    {
-      SubsequenceRecorder r(context, content1);
+      {
+        SubsequenceRecorder r(context, content1);
+        GetPaintController().UpdateCurrentPaintChunkProperties(
+            &content1_id, content1_properties);
+        DrawRect(context, content1, kBackgroundType,
+                 IntRect(100, 100, 50, 200));
+        DrawRect(context, content1, kForegroundType,
+                 IntRect(100, 100, 50, 200));
+      }
       GetPaintController().UpdateCurrentPaintChunkProperties(
-          &content1_id, content1_properties);
-      DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-      DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
+          &container1_foreground_id, container1_foreground_properties);
+      DrawRect(context, container1, kForegroundType,
+               IntRect(100, 100, 100, 100));
     }
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container1_foreground_id, container1_foreground_properties);
-    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  }
-  {
-    SubsequenceRecorder r(context, container2);
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container2_background_id, container2_background_properties);
-    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
     {
-      SubsequenceRecorder r(context, content2);
+      SubsequenceRecorder r(context, container2);
       GetPaintController().UpdateCurrentPaintChunkProperties(
-          &content2_id, content2_properties);
-      DrawRect(context, content2, kBackgroundType, IntRect(100, 200, 50, 200));
+          &container2_background_id, container2_background_properties);
+      DrawRect(context, container2, kBackgroundType,
+               IntRect(100, 200, 100, 100));
+      {
+        SubsequenceRecorder r(context, content2);
+        GetPaintController().UpdateCurrentPaintChunkProperties(
+            &content2_id, content2_properties);
+        DrawRect(context, content2, kBackgroundType,
+                 IntRect(100, 200, 50, 200));
+      }
     }
   }
-  CommitAndFinishCycle();
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -1171,58 +1262,62 @@
                                container2_background_properties),
                   IsPaintChunk(5, 6, content2_id, content2_properties)));
 
-  // Invalidate container1 but not content1.
-  container1.Invalidate();
-  // Container2 itself now becomes empty (but still has the 'content2' child),
-  // and chooses not to output subsequence info.
-  container2.Invalidate();
-  content2.Invalidate();
-  EXPECT_FALSE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container2));
-  EXPECT_FALSE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content2));
-  // Content2 now outputs foreground only.
   {
-    SubsequenceRecorder r(context, content2);
-    GetPaintController().UpdateCurrentPaintChunkProperties(&content2_id,
-                                                           content2_properties);
-    DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
-  }
-  // Repaint container1 with foreground only.
-  {
-    SubsequenceRecorder r(context, container1);
+    CommitCycleScope cycle_scope(GetPaintController());
+    // Invalidate container1 but not content1.
+    container1.Invalidate();
+    // Container2 itself now becomes empty (but still has the 'content2' child),
+    // and chooses not to output subsequence info.
+    container2.Invalidate();
+    content2.Invalidate();
     EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container1));
-    // Use cached subsequence of content1.
-    if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
-      // When under-invalidation-checking is enabled,
-      // UseCachedSubsequenceIfPossible is forced off, and the client is
-      // expected to create the same painting as in the previous paint.
-      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-          context, content1));
-      SubsequenceRecorder r(context, content1);
+        context, container2));
+    EXPECT_FALSE(
+        SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content2));
+    // Content2 now outputs foreground only.
+    {
+      SubsequenceRecorder r(context, content2);
       GetPaintController().UpdateCurrentPaintChunkProperties(
-          &content1_id, content1_properties);
-      DrawRect(context, content1, kBackgroundType, IntRect(100, 100, 50, 200));
-      DrawRect(context, content1, kForegroundType, IntRect(100, 100, 50, 200));
-    } else {
-      EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-          context, content1));
+          &content2_id, content2_properties);
+      DrawRect(context, content2, kForegroundType, IntRect(100, 200, 50, 200));
     }
-    GetPaintController().UpdateCurrentPaintChunkProperties(
-        &container1_foreground_id, container1_foreground_properties);
-    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  }
+    // Repaint container1 with foreground only.
+    {
+      SubsequenceRecorder r(context, container1);
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container1));
+      // Use cached subsequence of content1.
+      if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+        // When under-invalidation-checking is enabled,
+        // UseCachedSubsequenceIfPossible is forced off, and the client is
+        // expected to create the same painting as in the previous paint.
+        EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, content1));
+        SubsequenceRecorder r(context, content1);
+        GetPaintController().UpdateCurrentPaintChunkProperties(
+            &content1_id, content1_properties);
+        DrawRect(context, content1, kBackgroundType,
+                 IntRect(100, 100, 50, 200));
+        DrawRect(context, content1, kForegroundType,
+                 IntRect(100, 100, 50, 200));
+      } else {
+        EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, content1));
+      }
+      GetPaintController().UpdateCurrentPaintChunkProperties(
+          &container1_foreground_id, container1_foreground_properties);
+      DrawRect(context, container1, kForegroundType,
+               IntRect(100, 100, 100, 100));
+    }
 
-  EXPECT_EQ(2u, NumCachedNewItems());
-  EXPECT_EQ(1u, NumCachedNewSubsequences());
+    EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(1u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(0u, NumIndexedItems());
-  EXPECT_EQ(0u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(0u, NumIndexedItems());
+    EXPECT_EQ(0u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&content2, kForegroundType),
@@ -1248,10 +1343,8 @@
 
   FakeDisplayItemClient root("root");
   auto properties = DefaultPaintChunkProperties();
-  PaintChunk::Id root_id(root, DisplayItem::kLayerChunk);
   GraphicsContext context(GetPaintController());
-  GetPaintController().UpdateCurrentPaintChunkProperties(&root_id, properties);
-
+  PaintChunk::Id root_id(root, DisplayItem::kLayerChunk);
   FakeDisplayItemClient container1("container1");
   PaintChunk::Id container1_bg_id(container1, kBackgroundType);
   PaintChunk::Id container1_fg_id(container1, kForegroundType);
@@ -1265,36 +1358,47 @@
   PaintChunk::Id content2a_id(content2a, kBackgroundType);
   FakeDisplayItemClient content2b("content2b");
   PaintChunk::Id content2b_id(content2b, kForegroundType);
-
   {
-    SubsequenceRecorder r(context, container1);
-    DrawRect(context, container1, kBackgroundType, IntRect(100, 100, 100, 100));
-    {
-      SubsequenceRecorder r(context, content1a);
-      DrawRect(context, content1a, kBackgroundType, IntRect(100, 100, 50, 200));
-    }
-    {
-      SubsequenceRecorder r(context, content1b);
-      DrawRect(context, content1b, kForegroundType, IntRect(100, 100, 50, 200));
-    }
-    DrawRect(context, container1, kForegroundType, IntRect(100, 100, 100, 100));
-  }
-  {
-    SubsequenceRecorder r(context, container2);
-    DrawRect(context, container2, kBackgroundType, IntRect(100, 200, 100, 100));
-    {
-      SubsequenceRecorder r(context, content2a);
-      DrawRect(context, content2a, kBackgroundType, IntRect(100, 200, 50, 200));
-    }
-    {
-      SubsequenceRecorder r(context, content2b);
-      DrawRect(context, content2b, kForegroundType, IntRect(100, 200, 50, 200));
-    }
-  }
+    CommitCycleScope cycle_scope(GetPaintController());
+    GetPaintController().UpdateCurrentPaintChunkProperties(&root_id,
+                                                           properties);
 
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+    {
+      SubsequenceRecorder r(context, container1);
+      DrawRect(context, container1, kBackgroundType,
+               IntRect(100, 100, 100, 100));
+      {
+        SubsequenceRecorder r(context, content1a);
+        DrawRect(context, content1a, kBackgroundType,
+                 IntRect(100, 100, 50, 200));
+      }
+      {
+        SubsequenceRecorder r(context, content1b);
+        DrawRect(context, content1b, kForegroundType,
+                 IntRect(100, 100, 50, 200));
+      }
+      DrawRect(context, container1, kForegroundType,
+               IntRect(100, 100, 100, 100));
+    }
+    {
+      SubsequenceRecorder r(context, container2);
+      DrawRect(context, container2, kBackgroundType,
+               IntRect(100, 200, 100, 100));
+      {
+        SubsequenceRecorder r(context, content2a);
+        DrawRect(context, content2a, kBackgroundType,
+                 IntRect(100, 200, 50, 200));
+      }
+      {
+        SubsequenceRecorder r(context, content2b);
+        DrawRect(context, content2b, kForegroundType,
+                 IntRect(100, 200, 50, 200));
+      }
+    }
+
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -1322,14 +1426,16 @@
                           IsPaintChunk(6, 7, content2b_id, properties)));
 
   // Nothing invalidated. Should keep all subsequences.
-  EXPECT_TRUE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container1));
-  EXPECT_TRUE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container2));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+        context, container1));
+    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+        context, container2));
 
-  EXPECT_EQ(7u, NumCachedNewItems());
-  EXPECT_EQ(6u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+    EXPECT_EQ(7u, NumCachedNewItems());
+    EXPECT_EQ(6u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container1, kBackgroundType),
@@ -1358,14 +1464,16 @@
 
   // Swap order of the subsequences of container1 and container2.
   // Nothing invalidated. Should keep all subsequences.
-  EXPECT_TRUE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container2));
-  EXPECT_TRUE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container1));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+        context, container2));
+    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+        context, container1));
 
-  EXPECT_EQ(7u, NumCachedNewItems());
-  EXPECT_EQ(6u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+    EXPECT_EQ(7u, NumCachedNewItems());
+    EXPECT_EQ(6u, NumCachedNewSubsequences());
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&container2, kBackgroundType),
@@ -1397,20 +1505,21 @@
   FakeDisplayItemClient multicol("multicol");
   FakeDisplayItemClient content("content");
   GraphicsContext context(GetPaintController());
-  InitRootChunk();
-
   IntRect rect1(100, 100, 50, 50);
   IntRect rect2(150, 100, 50, 50);
   IntRect rect3(200, 100, 50, 50);
 
-  DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
 
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect1);
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
+    DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
 
-  CommitAndFinishCycle();
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect1);
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&multicol, kBackgroundType),
@@ -1425,25 +1534,26 @@
   EXPECT_NE(record1, record2);
   EXPECT_DEFAULT_ROOT_CHUNK(3);
 
-  InitRootChunk();
-  // Draw again with nothing invalidated.
-  EXPECT_TRUE(ClientCacheIsValid(multicol));
-  DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    // Draw again with nothing invalidated.
+    EXPECT_TRUE(ClientCacheIsValid(multicol));
+    DrawRect(context, multicol, kBackgroundType, IntRect(100, 200, 100, 100));
 
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect1);
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect1);
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
 
-  EXPECT_EQ(1u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(1u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(0u, NumIndexedItems());
-  EXPECT_EQ(1u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(0u, NumIndexedItems());
+    EXPECT_EQ(1u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&multicol, kBackgroundType),
@@ -1457,31 +1567,32 @@
                 .GetPaintRecord());
   EXPECT_DEFAULT_ROOT_CHUNK(3);
 
-  InitRootChunk();
-  // Now the multicol becomes 3 columns and repaints.
-  multicol.Invalidate();
-  DrawRect(context, multicol, kBackgroundType, IntRect(100, 100, 100, 100));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    // Now the multicol becomes 3 columns and repaints.
+    multicol.Invalidate();
+    DrawRect(context, multicol, kBackgroundType, IntRect(100, 100, 100, 100));
 
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect1);
-  DrawRect(context, content, kForegroundType, rect2);
-  DrawRect(context, content, kForegroundType, rect3);
-  GetPaintController().EndSkippingCache();
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect1);
+    DrawRect(context, content, kForegroundType, rect2);
+    DrawRect(context, content, kForegroundType, rect3);
+    GetPaintController().EndSkippingCache();
 
-  // We should repaint everything on invalidation of the scope container.
-  const auto& display_item_list =
-      GetPaintController().GetNewPaintArtifactShared()->GetDisplayItemList();
-  EXPECT_THAT(display_item_list,
-              ElementsAre(IsSameId(&multicol, kBackgroundType),
-                          IsSameId(&content, kForegroundType),
-                          IsSameId(&content, kForegroundType),
-                          IsSameId(&content, kForegroundType)));
-  EXPECT_NE(record1,
-            To<DrawingDisplayItem>(display_item_list[1]).GetPaintRecord());
-  EXPECT_NE(record2,
-            To<DrawingDisplayItem>(display_item_list[2]).GetPaintRecord());
-
-  CommitAndFinishCycle();
+    // We should repaint everything on invalidation of the scope container.
+    const auto& display_item_list =
+        GetPaintController().GetNewPaintArtifactShared()->GetDisplayItemList();
+    EXPECT_THAT(display_item_list,
+                ElementsAre(IsSameId(&multicol, kBackgroundType),
+                            IsSameId(&content, kForegroundType),
+                            IsSameId(&content, kForegroundType),
+                            IsSameId(&content, kForegroundType)));
+    EXPECT_NE(record1,
+              To<DrawingDisplayItem>(display_item_list[1]).GetPaintRecord());
+    EXPECT_NE(record2,
+              To<DrawingDisplayItem>(display_item_list[2]).GetPaintRecord());
+  }
   EXPECT_DEFAULT_ROOT_CHUNK(4);
 }
 
@@ -1493,14 +1604,15 @@
   IntRect rect2(150, 100, 50, 50);
   IntRect rect3(200, 100, 50, 50);
 
-  InitRootChunk();
-  DrawRect(context, content, kBackgroundType, rect1);
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
-  DrawRect(context, content, kForegroundType, rect3);
-
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, content, kBackgroundType, rect1);
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+    DrawRect(context, content, kForegroundType, rect3);
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&content, kBackgroundType),
@@ -1522,23 +1634,24 @@
   EXPECT_EQ(PaintInvalidationReason::kUncacheable,
             content.GetPaintInvalidationReason());
 
-  InitRootChunk();
-  // Draw again with nothing invalidated.
-  DrawRect(context, content, kBackgroundType, rect1);
-  GetPaintController().BeginSkippingCache();
-  DrawRect(context, content, kForegroundType, rect2);
-  GetPaintController().EndSkippingCache();
-  DrawRect(context, content, kForegroundType, rect3);
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    // Draw again with nothing invalidated.
+    DrawRect(context, content, kBackgroundType, rect1);
+    GetPaintController().BeginSkippingCache();
+    DrawRect(context, content, kForegroundType, rect2);
+    GetPaintController().EndSkippingCache();
+    DrawRect(context, content, kForegroundType, rect3);
 
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  EXPECT_EQ(0u, NumIndexedItems());
-  EXPECT_EQ(0u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    EXPECT_EQ(0u, NumIndexedItems());
+    EXPECT_EQ(0u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&content, kBackgroundType),
@@ -1562,17 +1675,18 @@
   PaintChunk::Id chunk_id(chunk_client, DisplayItem::kLayerChunk);
   auto& paint_controller = GetPaintController();
 
-  GraphicsContext context(paint_controller);
-  paint_controller.BeginSkippingCache();
-  paint_controller.SetWillForceNewChunk(true);
-  paint_controller.UpdateCurrentPaintChunkProperties(&chunk_id, properties);
-  DrawRect(context, item_client, kBackgroundType, IntRect(0, 0, 100, 100));
-  paint_controller.SetWillForceNewChunk(true);
-  paint_controller.UpdateCurrentPaintChunkProperties(&chunk_id, properties);
-  DrawRect(context, item_client, kBackgroundType, IntRect(0, 0, 100, 100));
-  paint_controller.EndSkippingCache();
-
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(paint_controller);
+    GraphicsContext context(paint_controller);
+    paint_controller.BeginSkippingCache();
+    paint_controller.SetWillForceNewChunk(true);
+    paint_controller.UpdateCurrentPaintChunkProperties(&chunk_id, properties);
+    DrawRect(context, item_client, kBackgroundType, IntRect(0, 0, 100, 100));
+    paint_controller.SetWillForceNewChunk(true);
+    paint_controller.UpdateCurrentPaintChunkProperties(&chunk_id, properties);
+    DrawRect(context, item_client, kBackgroundType, IntRect(0, 0, 100, 100));
+    paint_controller.EndSkippingCache();
+  }
 
   EXPECT_THAT(paint_controller.GetDisplayItemList(),
               ElementsAre(IsSameId(&item_client, kBackgroundType),
@@ -1590,15 +1704,15 @@
 TEST_P(PaintControllerTest, SmallPaintControllerHasOnePaintChunk) {
   FakeDisplayItemClient client("test client");
 
-  InitRootChunk();
-  GraphicsContext context(GetPaintController());
-  DrawRect(context, client, kBackgroundType, IntRect(0, 0, 100, 100));
-
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    GraphicsContext context(GetPaintController());
+    DrawRect(context, client, kBackgroundType, IntRect(0, 0, 100, 100));
+  }
   EXPECT_THAT(GetPaintController().PaintChunks(),
               ElementsAre(IsPaintChunk(0, 1)));
 }
-
 void DrawPath(GraphicsContext& context,
               DisplayItemClient& client,
               DisplayItem::Type type,
@@ -1671,15 +1785,17 @@
   FakeDisplayItemClient fourth("fourth");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, third, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, fourth, kBackgroundType, IntRect(100, 100, 50, 50));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, third, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, fourth, kBackgroundType, IntRect(100, 100, 50, 50));
 
-  EXPECT_EQ(0u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
-  CommitAndFinishCycle();
+    EXPECT_EQ(0u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
+  }
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
                           IsSameId(&second, kBackgroundType),
@@ -1692,20 +1808,21 @@
 
   // Simulate that a composited scrolling element is scrolled down, and "first"
   // and "second" are scrolled out of the interest rect.
-  InitRootChunk();
-  DrawRect(context, third, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, fourth, kBackgroundType, IntRect(100, 100, 50, 50));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, third, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, fourth, kBackgroundType, IntRect(100, 100, 50, 50));
 
-  EXPECT_EQ(2u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  // We indexed "first" and "second" when finding the cached item for "third".
-  EXPECT_EQ(2u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    // We indexed "first" and "second" when finding the cached item for "third".
+    EXPECT_EQ(2u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&third, kBackgroundType),
                           IsSameId(&fourth, kBackgroundType)));
@@ -1715,22 +1832,23 @@
   EXPECT_TRUE(fourth.IsValid());
 
   // Simulate "first" and "second" are scrolled back into the interest rect.
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
-  DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
-  DrawRect(context, third, kBackgroundType, IntRect(100, 100, 100, 100));
-  DrawRect(context, fourth, kBackgroundType, IntRect(100, 100, 50, 50));
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(100, 100, 300, 300));
+    DrawRect(context, second, kBackgroundType, IntRect(100, 100, 200, 200));
+    DrawRect(context, third, kBackgroundType, IntRect(100, 100, 100, 100));
+    DrawRect(context, fourth, kBackgroundType, IntRect(100, 100, 50, 50));
 
-  EXPECT_EQ(2u, NumCachedNewItems());
-  EXPECT_EQ(0u, NumCachedNewSubsequences());
+    EXPECT_EQ(2u, NumCachedNewItems());
+    EXPECT_EQ(0u, NumCachedNewSubsequences());
 #if DCHECK_IS_ON()
-  // We indexed "third" and "fourth" when finding the cached item for "first".
-  EXPECT_EQ(2u, NumIndexedItems());
-  EXPECT_EQ(2u, NumSequentialMatches());
-  EXPECT_EQ(0u, NumOutOfOrderMatches());
+    // We indexed "third" and "fourth" when finding the cached item for "first".
+    EXPECT_EQ(2u, NumIndexedItems());
+    EXPECT_EQ(2u, NumSequentialMatches());
+    EXPECT_EQ(0u, NumOutOfOrderMatches());
 #endif
-
-  CommitAndFinishCycle();
+  }
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&first, kBackgroundType),
                           IsSameId(&second, kBackgroundType),
@@ -1767,25 +1885,28 @@
   EXPECT_TRUE(cacheable.IsCacheable());
   EXPECT_FALSE(uncacheable.IsCacheable());
 
-  InitRootChunk();
   {
-    SubsequenceRecorder recorder(context, cacheable);
-    DrawRect(context, cacheable, kBackgroundType, IntRect(r));
-    DrawRect(context, uncacheable, kBackgroundType, IntRect(r));
-    // This should not trigger the duplicated id assert.
-    DrawRect(context, uncacheable, kBackgroundType, IntRect(r));
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    {
+      SubsequenceRecorder recorder(context, cacheable);
+      DrawRect(context, cacheable, kBackgroundType, IntRect(r));
+      DrawRect(context, uncacheable, kBackgroundType, IntRect(r));
+      // This should not trigger the duplicated id assert.
+      DrawRect(context, uncacheable, kBackgroundType, IntRect(r));
+    }
   }
-
-  CommitAndFinishCycle();
   EXPECT_TRUE(GetPaintController().GetDisplayItemList()[0].IsCacheable());
   EXPECT_FALSE(GetPaintController().GetDisplayItemList()[1].IsCacheable());
   EXPECT_FALSE(GetPaintController().GetDisplayItemList()[2].IsCacheable());
   EXPECT_TRUE(cacheable.IsCacheable());
   EXPECT_FALSE(uncacheable.IsCacheable());
 
-  InitRootChunk();
-  EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(cacheable));
-  CommitAndFinishCycle();
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(cacheable));
+  }
   EXPECT_TRUE(GetPaintController().GetDisplayItemList()[0].IsCacheable());
   EXPECT_FALSE(GetPaintController().GetDisplayItemList()[1].IsCacheable());
   EXPECT_FALSE(GetPaintController().GetDisplayItemList()[2].IsCacheable());
@@ -1798,19 +1919,22 @@
 
 TEST_P(PaintControllerTest, DuplicatedSubsequences) {
   FakeDisplayItemClient client("test");
-  GraphicsContext context(GetPaintController());
+  PaintController& controller = GetPaintController();
+  GraphicsContext context(controller);
 
   auto paint_duplicated_subsequences = [&]() {
-    InitRootChunk();
     {
-      SubsequenceRecorder r(context, client);
-      DrawRect(context, client, kBackgroundType, IntRect(100, 100, 100, 100));
+      CommitCycleScope cycle_scope(controller);
+      InitRootChunk();
+      {
+        SubsequenceRecorder r(context, client);
+        DrawRect(context, client, kBackgroundType, IntRect(100, 100, 100, 100));
+      }
+      {
+        SubsequenceRecorder r(context, client);
+        DrawRect(context, client, kForegroundType, IntRect(100, 100, 100, 100));
+      }
     }
-    {
-      SubsequenceRecorder r(context, client);
-      DrawRect(context, client, kForegroundType, IntRect(100, 100, 100, 100));
-    }
-    CommitAndFinishCycle();
   };
 
 #if DCHECK_IS_ON()
@@ -1818,27 +1942,29 @@
                "Multiple subsequences for client: \"test\"");
 #else
   // The following is for non-DCHECK path. No security CHECK should trigger.
-  paint_duplicated_subsequences();
-  // Paint again.
-  InitRootChunk();
-  if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
-    EXPECT_FALSE(GetPaintController().UseCachedSubsequenceIfPossible(client));
-    SubsequenceRecorder r(context, client);
-    DrawRect(context, client, kBackgroundType, IntRect(100, 100, 100, 100));
-  } else {
-    EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(client));
-  }
   {
-    // Should not use the cached duplicated subsequence.
-    EXPECT_FALSE(GetPaintController().UseCachedSubsequenceIfPossible(client));
-    SubsequenceRecorder r(context, client);
-    DrawRect(context, client, kForegroundType, IntRect(100, 100, 100, 100));
+    CommitCycleScope cycle_scope(GetPaintController());
+    paint_duplicated_subsequences();
+    // Paint again.
+    InitRootChunk();
+    if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+      EXPECT_FALSE(GetPaintController().UseCachedSubsequenceIfPossible(client));
+      SubsequenceRecorder r(context, client);
+      DrawRect(context, client, kBackgroundType, IntRect(100, 100, 100, 100));
+    } else {
+      EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(client));
+    }
+    {
+      // Should not use the cached duplicated subsequence.
+      EXPECT_FALSE(GetPaintController().UseCachedSubsequenceIfPossible(client));
+      SubsequenceRecorder r(context, client);
+      DrawRect(context, client, kForegroundType, IntRect(100, 100, 100, 100));
+    }
   }
-  CommitAndFinishCycle();
 #endif
 }
 
-TEST_P(PaintControllerTest, DeletedClientInUnderInvaldiatedSubsequence) {
+TEST_P(PaintControllerTest, DeletedClientInUnderInvalidatedSubsequence) {
   if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
     return;
 
@@ -1846,26 +1972,30 @@
   auto content = std::make_unique<FakeDisplayItemClient>("content");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
   {
-    SubsequenceRecorder r(context, container);
-    DrawRect(context, *content, kBackgroundType, IntRect(100, 100, 300, 300));
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    {
+      SubsequenceRecorder r(context, container);
+      DrawRect(context, *content, kBackgroundType, IntRect(100, 100, 300, 300));
+    }
   }
-  CommitAndFinishCycle();
 
   content = nullptr;
-  InitRootChunk();
-  // Leave container not invalidated.
+  {
+    CommitCycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    // Leave container not invalidated.
 #if DCHECK_IS_ON()
-  ASSERT_DEATH(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container),
-      "");
+    ASSERT_DEATH(
+        SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container),
+        "");
 #else
-  // This should not crash.
-  EXPECT_TRUE(
-      SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container));
-  CommitAndFinishCycle();
+    // This should not crash.
+    EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(context,
+                                                                    container));
 #endif
+  }
 }
 
 #endif  // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
index ca7223d5..f8839d0 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
@@ -80,11 +80,6 @@
 
   void InvalidateAll() { paint_controller_->InvalidateAllForTesting(); }
 
-  void CommitAndFinishCycle() {
-    paint_controller_->CommitNewDisplayItems();
-    paint_controller_->FinishCycle();
-  }
-
   using SubsequenceMarkers = PaintController::SubsequenceMarkers;
   const SubsequenceMarkers* GetSubsequenceMarkers(
       const DisplayItemClient& client) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
index bfb86a5..a161110e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
@@ -20,16 +20,13 @@
 }
 
 PaintRecordBuilder::PaintRecordBuilder(PaintController& paint_controller)
-    : paint_controller_(&paint_controller), context_(*paint_controller_) {
-  paint_controller.ReserveCapacity();
-}
+    : paint_controller_(&paint_controller), context_(*paint_controller_) {}
 
 PaintRecordBuilder::~PaintRecordBuilder() = default;
 
 sk_sp<PaintRecord> PaintRecordBuilder::EndRecording(
     const PropertyTreeState& replay_state) {
   paint_controller_->CommitNewDisplayItems();
-  paint_controller_->FinishCycle();
   return paint_controller_->GetPaintArtifact().GetPaintRecord(replay_state);
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
index efd5eb3cc..c7829348 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
@@ -36,34 +36,40 @@
 }
 
 TEST_F(PaintRecordBuilderTest, LastingPaintController) {
-  InitRootChunk();
-
+  FakeDisplayItemClient client("client");
   PaintRecordBuilder builder(GetPaintController());
   auto& context = builder.Context();
-  EXPECT_EQ(&context.GetPaintController(), &GetPaintController());
-
-  FakeDisplayItemClient client("client");
-  DrawRect(context, client, kBackgroundType, IntRect(10, 10, 20, 20));
-  DrawRect(context, client, kForegroundType, IntRect(15, 15, 10, 10));
-  EXPECT_FALSE(ClientCacheIsValid(client));
-
   MockPaintCanvas canvas;
   PaintFlags flags;
-  EXPECT_CALL(canvas, drawPicture(_)).Times(1);
-  builder.EndRecording(canvas);
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+
+    EXPECT_EQ(&context.GetPaintController(), &GetPaintController());
+
+    DrawRect(context, client, kBackgroundType, IntRect(10, 10, 20, 20));
+    DrawRect(context, client, kForegroundType, IntRect(15, 15, 10, 10));
+    EXPECT_FALSE(ClientCacheIsValid(client));
+
+    EXPECT_CALL(canvas, drawPicture(_)).Times(1);
+    builder.EndRecording(canvas);
+  }
   EXPECT_TRUE(ClientCacheIsValid(client));
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kBackgroundType),
                           IsSameId(&client, kForegroundType)));
 
-  InitRootChunk();
-  EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, client,
-                                                          kBackgroundType));
-  EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, client,
-                                                          kForegroundType));
-  EXPECT_CALL(canvas, drawPicture(_)).Times(1);
-  builder.EndRecording(canvas);
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, client,
+                                                            kBackgroundType));
+    EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, client,
+                                                            kForegroundType));
+    EXPECT_CALL(canvas, drawPicture(_)).Times(1);
+    builder.EndRecording(canvas);
+  }
 
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kBackgroundType),
@@ -73,21 +79,27 @@
 
 TEST_F(PaintRecordBuilderTest, TransientAndAnotherPaintController) {
   GraphicsContext context(GetPaintController());
-
-  InitRootChunk();
   FakeDisplayItemClient client("client");
-  DrawRect(context, client, kBackgroundType, IntRect(10, 10, 20, 20));
-  DrawRect(context, client, kForegroundType, IntRect(15, 15, 10, 10));
-  CommitAndFinishCycle();
+  PaintRecordBuilder builder;
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, client, kBackgroundType, IntRect(10, 10, 20, 20));
+    DrawRect(context, client, kForegroundType, IntRect(15, 15, 10, 10));
+    GetPaintController().CommitNewDisplayItems();
+  }
   EXPECT_THAT(GetPaintController().GetDisplayItemList(),
               ElementsAre(IsSameId(&client, kBackgroundType),
                           IsSameId(&client, kForegroundType)));
   EXPECT_TRUE(ClientCacheIsValid(client));
 
-  PaintRecordBuilder builder;
-  EXPECT_NE(&builder.Context().GetPaintController(), &GetPaintController());
-  DrawRect(builder.Context(), client, kBackgroundType, IntRect(10, 10, 20, 20));
-  builder.EndRecording();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    EXPECT_NE(&builder.Context().GetPaintController(), &GetPaintController());
+    DrawRect(builder.Context(), client, kBackgroundType,
+             IntRect(10, 10, 20, 20));
+    builder.EndRecording();
+  }
 
   // The transient PaintController in PaintRecordBuilder doesn't affect the
   // client's cache status in another PaintController.
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_under_invalidation_checker_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_under_invalidation_checker_test.cc
index 1b22a95..f93aba6 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_under_invalidation_checker_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_under_invalidation_checker_test.cc
@@ -29,15 +29,21 @@
     FakeDisplayItemClient first("first");
     GraphicsContext context(GetPaintController());
 
-    InitRootChunk();
-    DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-    DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
-    CommitAndFinishCycle();
+    {
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+      DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      GetPaintController().CommitNewDisplayItems();
+    }
 
-    InitRootChunk();
-    DrawRect(context, first, kBackgroundType, IntRect(2, 2, 3, 3));
-    DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
-    CommitAndFinishCycle();
+    {
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      DrawRect(context, first, kBackgroundType, IntRect(2, 2, 3, 3));
+      DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      GetPaintController().CommitNewDisplayItems();
+    }
   };
 
   EXPECT_DEATH(test(),
@@ -55,14 +61,20 @@
   FakeDisplayItemClient first("first");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+    GetPaintController().CommitNewDisplayItems();
+  }
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-  DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+    DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+    GetPaintController().CommitNewDisplayItems();
+  }
 }
 
 TEST_F(PaintControllerUnderInvalidationTest, LessDrawing) {
@@ -71,37 +83,49 @@
   FakeDisplayItemClient first("first");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-  DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+    DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+    GetPaintController().CommitNewDisplayItems();
+  }
 
-  InitRootChunk();
-  DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-  CommitAndFinishCycle();
+  {
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+    GetPaintController().CommitNewDisplayItems();
+  }
 }
 
 TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawingInSubsequence) {
   auto test = [&]() {
     FakeDisplayItemClient first("first");
     GraphicsContext context(GetPaintController());
-    InitRootChunk();
     {
-      SubsequenceRecorder r(context, first);
-      DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-      DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        SubsequenceRecorder r(context, first);
+        DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+        DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
 
-    InitRootChunk();
     {
-      EXPECT_FALSE(
-          SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
-      SubsequenceRecorder r(context, first);
-      DrawRect(context, first, kBackgroundType, IntRect(2, 2, 1, 1));
-      DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, first));
+        SubsequenceRecorder r(context, first);
+        DrawRect(context, first, kBackgroundType, IntRect(2, 2, 1, 1));
+        DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
   };
 
   EXPECT_DEATH(test(),
@@ -119,22 +143,28 @@
     FakeDisplayItemClient first("first");
     GraphicsContext context(GetPaintController());
 
-    InitRootChunk();
     {
-      SubsequenceRecorder r(context, first);
-      DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        SubsequenceRecorder r(context, first);
+        DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
 
-    InitRootChunk();
     {
-      EXPECT_FALSE(
-          SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
-      SubsequenceRecorder r(context, first);
-      DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
-      DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, first));
+        SubsequenceRecorder r(context, first);
+        DrawRect(context, first, kBackgroundType, IntRect(1, 1, 1, 1));
+        DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
   };
 
   EXPECT_DEATH(test(),
@@ -151,22 +181,28 @@
     FakeDisplayItemClient first("first");
     GraphicsContext context(GetPaintController());
 
-    InitRootChunk();
     {
-      SubsequenceRecorder r(context, first);
-      DrawRect(context, first, kBackgroundType, IntRect(1, 1, 3, 3));
-      DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        SubsequenceRecorder r(context, first);
+        DrawRect(context, first, kBackgroundType, IntRect(1, 1, 3, 3));
+        DrawRect(context, first, kForegroundType, IntRect(1, 1, 3, 3));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
 
-    InitRootChunk();
     {
-      EXPECT_FALSE(
-          SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
-      SubsequenceRecorder r(context, first);
-      DrawRect(context, first, kBackgroundType, IntRect(1, 1, 3, 3));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, first));
+        SubsequenceRecorder r(context, first);
+        DrawRect(context, first, kBackgroundType, IntRect(1, 1, 3, 3));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
   };
 
   EXPECT_DEATH(test(),
@@ -182,23 +218,29 @@
   FakeDisplayItemClient content("content");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
   {
-    SubsequenceRecorder r(context, container);
-    DrawRect(context, content, kBackgroundType, IntRect(1, 1, 3, 3));
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    {
+      SubsequenceRecorder r(context, container);
+      DrawRect(context, content, kBackgroundType, IntRect(1, 1, 3, 3));
+    }
+    GetPaintController().CommitNewDisplayItems();
   }
-  CommitAndFinishCycle();
 
   content.Invalidate();
-  InitRootChunk();
-  // Leave container not invalidated.
   {
-    EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container));
-    SubsequenceRecorder r(context, container);
-    DrawRect(context, content, kBackgroundType, IntRect(1, 1, 3, 3));
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    // Leave container not invalidated.
+    {
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container));
+      SubsequenceRecorder r(context, container);
+      DrawRect(context, content, kBackgroundType, IntRect(1, 1, 3, 3));
+    }
+    GetPaintController().CommitNewDisplayItems();
   }
-  CommitAndFinishCycle();
 }
 
 TEST_F(PaintControllerUnderInvalidationTest, SubsequenceBecomesEmpty) {
@@ -206,20 +248,26 @@
     FakeDisplayItemClient target("target");
     GraphicsContext context(GetPaintController());
 
-    InitRootChunk();
     {
-      SubsequenceRecorder r(context, target);
-      DrawRect(context, target, kBackgroundType, IntRect(1, 1, 3, 3));
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        SubsequenceRecorder r(context, target);
+        DrawRect(context, target, kBackgroundType, IntRect(1, 1, 3, 3));
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
 
-    InitRootChunk();
     {
-      EXPECT_FALSE(
-          SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, target));
-      SubsequenceRecorder r(context, target);
+      PaintController::CycleScope cycle_scope(GetPaintController());
+      InitRootChunk();
+      {
+        EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+            context, target));
+        SubsequenceRecorder r(context, target);
+      }
+      GetPaintController().CommitNewDisplayItems();
     }
-    CommitAndFinishCycle();
   };
 
   EXPECT_DEATH(test(),
@@ -232,29 +280,35 @@
   FakeDisplayItemClient content("content");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
   {
-    SubsequenceRecorder r(context, container);
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
     {
-      DisplayItemCacheSkipper cache_skipper(context);
-      DrawRect(context, content, kBackgroundType, IntRect(1, 1, 3, 3));
+      SubsequenceRecorder r(context, container);
+      {
+        DisplayItemCacheSkipper cache_skipper(context);
+        DrawRect(context, content, kBackgroundType, IntRect(1, 1, 3, 3));
+      }
+      DrawRect(context, content, kForegroundType, IntRect(2, 2, 4, 4));
     }
-    DrawRect(context, content, kForegroundType, IntRect(2, 2, 4, 4));
+    GetPaintController().CommitNewDisplayItems();
   }
-  CommitAndFinishCycle();
 
-  InitRootChunk();
   {
-    EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container));
-    SubsequenceRecorder r(context, container);
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
     {
-      DisplayItemCacheSkipper cache_skipper(context);
-      DrawRect(context, content, kBackgroundType, IntRect(2, 2, 4, 4));
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container));
+      SubsequenceRecorder r(context, container);
+      {
+        DisplayItemCacheSkipper cache_skipper(context);
+        DrawRect(context, content, kBackgroundType, IntRect(2, 2, 4, 4));
+      }
+      DrawRect(context, content, kForegroundType, IntRect(2, 2, 4, 4));
     }
-    DrawRect(context, content, kForegroundType, IntRect(2, 2, 4, 4));
+    GetPaintController().CommitNewDisplayItems();
   }
-  CommitAndFinishCycle();
 }
 
 TEST_F(PaintControllerUnderInvalidationTest,
@@ -263,27 +317,33 @@
   FakeDisplayItemClient content("content");
   GraphicsContext context(GetPaintController());
 
-  InitRootChunk();
   {
-    SubsequenceRecorder r(context, container);
-    DrawRect(context, container, kBackgroundType, IntRect(1, 1, 3, 3));
-    { SubsequenceRecorder r1(context, content); }
-    DrawRect(context, container, kForegroundType, IntRect(1, 1, 3, 3));
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    {
+      SubsequenceRecorder r(context, container);
+      DrawRect(context, container, kBackgroundType, IntRect(1, 1, 3, 3));
+      { SubsequenceRecorder r1(context, content); }
+      DrawRect(context, container, kForegroundType, IntRect(1, 1, 3, 3));
+    }
+    GetPaintController().CommitNewDisplayItems();
   }
-  CommitAndFinishCycle();
 
-  InitRootChunk();
   {
-    EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-        context, container));
-    SubsequenceRecorder r(context, container);
-    DrawRect(context, container, kBackgroundType, IntRect(1, 1, 3, 3));
-    EXPECT_FALSE(
-        SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content));
-    { SubsequenceRecorder r1(context, content); }
-    DrawRect(context, container, kForegroundType, IntRect(1, 1, 3, 3));
+    PaintController::CycleScope cycle_scope(GetPaintController());
+    InitRootChunk();
+    {
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, container));
+      SubsequenceRecorder r(context, container);
+      DrawRect(context, container, kBackgroundType, IntRect(1, 1, 3, 3));
+      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+          context, content));
+      { SubsequenceRecorder r1(context, content); }
+      DrawRect(context, container, kForegroundType, IntRect(1, 1, 3, 3));
+    }
+    GetPaintController().CommitNewDisplayItems();
   }
-  CommitAndFinishCycle();
 }
 
 #endif  // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
diff --git a/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h b/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h
index b8a22435..9781d7e9 100644
--- a/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h
+++ b/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h
@@ -37,6 +37,7 @@
   SubsequenceRecorder(GraphicsContext& context, const DisplayItemClient& client)
       : paint_controller_(context.GetPaintController()) {
     subsequence_index_ = paint_controller_.BeginSubsequence(client);
+    paint_controller_.MarkClientForValidation(client);
   }
 
   SubsequenceRecorder(const SubsequenceRecorder&) = delete;
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
index e13e7fc..3608238f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -40,9 +40,7 @@
 class RawResourceClient;
 class ResourceFetcher;
 
-// TODO(chromium:1210399): Make RawResource final again once
-// ResourceLoaderCodeCacheTest is moved to blink_unittests.
-class PLATFORM_EXPORT RawResource : public Resource {
+class PLATFORM_EXPORT RawResource final : public Resource {
  public:
   static RawResource* FetchSynchronously(FetchParameters&,
                                          ResourceFetcher*,
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index af5d5d36..b9816c50 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -275,9 +275,6 @@
 
   // Gets whether the serialized cached metadata must contain a hash of the
   // source text. For resources other than ScriptResource, this is always false.
-  // TODO(chromium:1210399): This can be true in non-ScriptResource in unit
-  // tests. Fix this once ResourceLoaderCodeCacheTest is moved to
-  // blink_unittests.
   virtual bool CodeCacheHashRequired() const;
 
   AtomicString HttpContentType() const;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
index e6ff4ec5..09fd868f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -29,8 +29,8 @@
 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/testing/code_cache_loader_mock.h"
 #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h"
+#include "third_party/blink/renderer/platform/testing/noop_web_url_loader.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
-#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -78,8 +78,7 @@
   const KURL foo_url_;
   const KURL bar_url_;
 
-  class NoopLoaderFactory : public ResourceFetcher::LoaderFactory {
-   public:
+  class NoopLoaderFactory final : public ResourceFetcher::LoaderFactory {
     std::unique_ptr<WebURLLoader> CreateURLLoader(
         const ResourceRequest& request,
         const ResourceLoaderOptions& options,
@@ -101,61 +100,13 @@
 
   ResourceFetcher* MakeResourceFetcher(
       TestResourceFetcherProperties* properties,
-      FetchContext* context,
-      ResourceFetcher::LoaderFactory* loader_factory = nullptr) {
+      FetchContext* context) {
     return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
         properties->MakeDetachable(), context, CreateTaskRunner(),
-        CreateTaskRunner(),
-        loader_factory ? loader_factory
-                       : MakeGarbageCollected<NoopLoaderFactory>(),
+        CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(),
         MakeGarbageCollected<MockContextLifecycleNotifier>(),
         nullptr /* back_forward_cache_loader_helper */));
   }
-
- private:
-  class NoopWebURLLoader final : public WebURLLoader {
-   public:
-    explicit NoopWebURLLoader(
-        scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-        : task_runner_(task_runner) {}
-    ~NoopWebURLLoader() override = default;
-    void LoadSynchronously(
-        std::unique_ptr<network::ResourceRequest> request,
-        scoped_refptr<WebURLRequestExtraData> url_request_extra_data,
-        bool pass_response_pipe_to_client,
-        bool no_mime_sniffing,
-        base::TimeDelta timeout_interval,
-        WebURLLoaderClient*,
-        WebURLResponse&,
-        absl::optional<WebURLError>&,
-        WebData&,
-        int64_t& encoded_data_length,
-        int64_t& encoded_body_length,
-        WebBlobInfo& downloaded_blob,
-        std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
-            resource_load_info_notifier_wrapper) override {
-      NOTREACHED();
-    }
-    void LoadAsynchronously(
-        std::unique_ptr<network::ResourceRequest> request,
-        scoped_refptr<WebURLRequestExtraData> url_request_extra_data,
-        bool no_mime_sniffing,
-        std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
-            resource_load_info_notifier_wrapper,
-        WebURLLoaderClient*) override {}
-
-    void Freeze(LoaderFreezeMode) override {}
-    void DidChangePriority(WebURLRequest::Priority, int) override {
-      NOTREACHED();
-    }
-    scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForBodyLoader()
-        override {
-      return task_runner_;
-    }
-
-   private:
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  };
 };
 
 std::ostream& operator<<(std::ostream& o, const ResourceLoaderTest::From& f) {
@@ -841,203 +792,4 @@
   ExpectHistogramsMatching(info);
 }
 
-// TODO(chromium:1210399): Move this to blink_unittests and use ScriptResource.
-class ResourceLoaderCodeCacheTest : public ResourceLoaderTest {
- public:
-  ResourceLoaderCodeCacheTest() = default;
-
- protected:
-  class CodeCacheTestLoaderFactory final : public NoopLoaderFactory {
-   public:
-    explicit CodeCacheTestLoaderFactory(
-        scoped_refptr<CodeCacheLoaderMock::Controller> controller)
-        : controller_(std::move(controller)) {}
-    std::unique_ptr<WebCodeCacheLoader> CreateCodeCacheLoader() override {
-      return std::make_unique<CodeCacheLoaderMock>(controller_);
-    }
-
-   private:
-    scoped_refptr<CodeCacheLoaderMock::Controller> controller_;
-  };
-
-  // A version of RawResource that overrides a few virtual methods so that we
-  // can observe how ResourceLoader is calling functions on its Resource.
-  class TestRawResource : public RawResource {
-   public:
-    TestRawResource(const ResourceRequest& resource_request,
-                    ResourceType type,
-                    const ResourceLoaderOptions& options)
-        : RawResource(resource_request, type, options) {}
-
-    bool CodeCacheHashRequired() const override {
-      return code_cache_hash_required_;
-    }
-    void SetSerializedCachedMetadata(mojo_base::BigBuffer data) override {
-      cached_metadata_ = std::move(data);
-    }
-
-    const mojo_base::BigBuffer* CachedMetadata() {
-      return cached_metadata_ ? &*cached_metadata_ : nullptr;
-    }
-
-    void SetCodeCacheHashRequired(bool code_cache_hash_required) {
-      code_cache_hash_required_ = code_cache_hash_required;
-    }
-
-   private:
-    bool code_cache_hash_required_ = false;
-    absl::optional<mojo_base::BigBuffer> cached_metadata_;
-  };
-
-  class TestRawResourceFactory : public NonTextResourceFactory {
-   public:
-    TestRawResourceFactory() : NonTextResourceFactory(ResourceType::kScript) {}
-
-    Resource* Create(const ResourceRequest& request,
-                     const ResourceLoaderOptions& options) const override {
-      return MakeGarbageCollected<TestRawResource>(request, type_, options);
-    }
-  };
-
-  // All relevant variables after running CommonSetup.
-  struct State {
-    STACK_ALLOCATED();
-
-   public:
-    TestRawResource* resource;
-    ResourceLoader* loader;
-    ResourceResponse response;
-    scoped_refptr<CodeCacheLoaderMock::Controller> controller;
-  };
-
-  State CommonSetup(const char* url_string = nullptr) {
-    SchemeRegistry::RegisterURLSchemeAsCodeCacheWithHashing(
-        "codecachewithhashing");
-
-    State state;
-    auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-    FetchContext* context = MakeGarbageCollected<MockFetchContext>();
-    state.controller = base::MakeRefCounted<CodeCacheLoaderMock::Controller>();
-    state.controller->DelayResponse();
-    auto* loader_factory =
-        MakeGarbageCollected<CodeCacheTestLoaderFactory>(state.controller);
-    auto* fetcher = MakeResourceFetcher(properties, context, loader_factory);
-
-    KURL url(url_string ? url_string
-                        : "codecachewithhashing://www.example.com/");
-    ResourceRequest request(url);
-    request.SetRequestContext(mojom::blink::RequestContextType::SCRIPT);
-
-    FetchParameters params = FetchParameters::CreateForTest(std::move(request));
-    state.resource = static_cast<TestRawResource*>(
-        fetcher->RequestResource(params, TestRawResourceFactory(), nullptr));
-    state.resource->SetCodeCacheHashRequired(true);
-    state.loader = state.resource->Loader();
-
-    state.response = ResourceResponse(url);
-    state.response.SetHttpStatusCode(200);
-
-    return state;
-  }
-};
-
-TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheEmptyResponseFirst) {
-  State state = CommonSetup();
-
-  state.loader->DidReceiveResponse(WrappedResourceResponse(state.response));
-
-  // Nothing has changed yet because the code cache hasn't yet responded.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-
-  // An empty code cache response means no data was found.
-  state.controller->Respond(base::Time(), mojo_base::BigBuffer());
-
-  // No code cache data was present.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-}
-
-TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheEmptyResponseSecond) {
-  State state = CommonSetup();
-
-  // An empty code cache response means no data was found.
-  state.controller->Respond(base::Time(), mojo_base::BigBuffer());
-
-  // Nothing has changed yet because the content response hasn't arrived yet.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-
-  state.loader->DidReceiveResponse(WrappedResourceResponse(state.response));
-
-  // No code cache data was present.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-}
-
-TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheFullResponseFirst) {
-  State state = CommonSetup();
-
-  state.loader->DidReceiveResponse(WrappedResourceResponse(state.response));
-
-  // Nothing has changed yet because the code cache hasn't yet responded.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-
-  const uint8_t data[] = {2, 3, 4, 5};
-  state.controller->Respond(base::Time(), mojo_base::BigBuffer(data));
-
-  // Code cache data was present.
-  ASSERT_TRUE(state.resource->CachedMetadata());
-  EXPECT_EQ(state.resource->CachedMetadata()->size(), 4UL);
-  EXPECT_EQ(state.resource->CachedMetadata()->data()[3], 5UL);
-}
-
-TEST_F(ResourceLoaderCodeCacheTest, WebUICodeCacheFullResponseSecond) {
-  State state = CommonSetup();
-
-  const uint8_t data[] = {2, 3, 4, 5};
-  state.controller->Respond(base::Time(), mojo_base::BigBuffer(data));
-
-  // Nothing has changed yet because the content response hasn't arrived yet.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-
-  state.loader->DidReceiveResponse(WrappedResourceResponse(state.response));
-
-  // Code cache data was present.
-  ASSERT_TRUE(state.resource->CachedMetadata());
-  EXPECT_EQ(state.resource->CachedMetadata()->size(), 4UL);
-  EXPECT_EQ(state.resource->CachedMetadata()->data()[3], 5UL);
-}
-
-TEST_F(ResourceLoaderCodeCacheTest,
-       WebUICodeCacheFullResponseSecondResourceDoesNotSupportHashing) {
-  State state = CommonSetup();
-  state.resource->SetCodeCacheHashRequired(false);
-
-  const uint8_t data[] = {2, 3, 4, 5};
-  state.controller->Respond(base::Time(), mojo_base::BigBuffer(data));
-
-  // Nothing has changed yet because the content response hasn't arrived yet.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-
-  state.loader->DidReceiveResponse(WrappedResourceResponse(state.response));
-
-  // Since the Resource didn't specify that a hash check is required,
-  // the cached metadata should not be set.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-}
-
-TEST_F(ResourceLoaderCodeCacheTest,
-       WebUICodeCacheFullResponseSecondHttpsScheme) {
-  State state = CommonSetup("https://www.example.com/");
-
-  const uint8_t data[] = {2, 3, 4, 5};
-  state.controller->Respond(base::Time(), mojo_base::BigBuffer(data));
-
-  // Nothing has changed yet because the content response hasn't arrived yet.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-
-  state.loader->DidReceiveResponse(WrappedResourceResponse(state.response));
-
-  // Since the URL was https, and the response times were not set, the cached
-  // metadata should not be set.
-  EXPECT_EQ(state.resource->CachedMetadata(), nullptr);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector.cc b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
index ec2da87..373c03c 100644
--- a/third_party/blink/renderer/platform/media/key_system_config_selector.cc
+++ b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
@@ -18,6 +19,7 @@
 #include "media/base/key_systems.h"
 #include "media/base/logging_override_if_enabled.h"
 #include "media/base/media_permission.h"
+#include "media/base/media_switches.h"
 #include "media/base/mime_util.h"
 #include "media/media_buildflags.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
@@ -168,7 +170,13 @@
   // avoid asking IsSupported*MediaFormat() about HEVC. EME support for HEVC
   // profiles is described via KeySystemProperties::GetSupportedCodecs().
   // TODO(1156282): Decouple the rest of clear vs EME codec support.
-  if (!use_aes_decryptor &&
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  const bool allow_hevc = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kLacrosEnablePlatformEncryptedHevc);
+#else
+  const bool allow_hevc = true;
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (allow_hevc && !use_aes_decryptor &&
       base::ToLowerASCII(container_mime_type) == "video/mp4" &&
       !codec_vector.empty()) {
     auto it = codec_vector.begin();
diff --git a/third_party/blink/renderer/platform/testing/noop_web_url_loader.cc b/third_party/blink/renderer/platform/testing/noop_web_url_loader.cc
new file mode 100644
index 0000000..a744186
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/noop_web_url_loader.cc
@@ -0,0 +1,39 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/noop_web_url_loader.h"
+
+#include "services/network/public/cpp/resource_request.h"
+#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
+#include "third_party/blink/public/platform/web_url_request_extra_data.h"
+
+namespace blink {
+
+void NoopWebURLLoader::LoadSynchronously(
+    std::unique_ptr<network::ResourceRequest> request,
+    scoped_refptr<WebURLRequestExtraData> url_request_extra_data,
+    bool pass_response_pipe_to_client,
+    bool no_mime_sniffing,
+    base::TimeDelta timeout_interval,
+    WebURLLoaderClient*,
+    WebURLResponse&,
+    absl::optional<WebURLError>&,
+    WebData&,
+    int64_t& encoded_data_length,
+    int64_t& encoded_body_length,
+    WebBlobInfo& downloaded_blob,
+    std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
+        resource_load_info_notifier_wrapper) {
+  NOTREACHED();
+}
+
+void NoopWebURLLoader::LoadAsynchronously(
+    std::unique_ptr<network::ResourceRequest> request,
+    scoped_refptr<WebURLRequestExtraData> url_request_extra_data,
+    bool no_mime_sniffing,
+    std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
+        resource_load_info_notifier_wrapper,
+    WebURLLoaderClient*) {}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/noop_web_url_loader.h b/third_party/blink/renderer/platform/testing/noop_web_url_loader.h
new file mode 100644
index 0000000..7283c5e6
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/noop_web_url_loader.h
@@ -0,0 +1,56 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_NOOP_WEB_URL_LOADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_NOOP_WEB_URL_LOADER_H_
+
+#include "third_party/blink/public/platform/web_url_loader.h"
+
+namespace blink {
+
+class NoopWebURLLoader final : public WebURLLoader {
+ public:
+  explicit NoopWebURLLoader(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : task_runner_(task_runner) {}
+  ~NoopWebURLLoader() override = default;
+  void LoadSynchronously(
+      std::unique_ptr<network::ResourceRequest> request,
+      scoped_refptr<WebURLRequestExtraData> url_request_extra_data,
+      bool pass_response_pipe_to_client,
+      bool no_mime_sniffing,
+      base::TimeDelta timeout_interval,
+      WebURLLoaderClient*,
+      WebURLResponse&,
+      absl::optional<WebURLError>&,
+      WebData&,
+      int64_t& encoded_data_length,
+      int64_t& encoded_body_length,
+      WebBlobInfo& downloaded_blob,
+      std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
+          resource_load_info_notifier_wrapper) override;
+  void LoadAsynchronously(
+      std::unique_ptr<network::ResourceRequest> request,
+      scoped_refptr<WebURLRequestExtraData> url_request_extra_data,
+      bool no_mime_sniffing,
+      std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
+          resource_load_info_notifier_wrapper,
+      WebURLLoaderClient*) override;
+
+  void Freeze(WebLoaderFreezeMode) override {}
+  void DidChangePriority(WebURLRequest::Priority, int) override {
+    NOTREACHED();
+  }
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForBodyLoader()
+      override {
+    return task_runner_;
+  }
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_NOOP_WEB_URL_LOADER_H_
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.cc b/third_party/blink/renderer/platform/weborigin/security_policy.cc
index 42210764..4ebbed3 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -30,10 +30,15 @@
 
 #include <memory>
 
+#include "base/command_line.h"
+#include "base/no_destructor.h"
 #include "base/strings/pattern.h"
+#include "base/strings/string_split.h"
+#include "build/build_config.h"
 #include "services/network/public/cpp/cors/origin_access_list.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/blink/public/common/loader/referrer_utils.h"
+#include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
@@ -306,4 +311,45 @@
   return true;
 }
 
+#if defined(OS_FUCHSIA)
+namespace {
+std::vector<url::Origin> GetSharedArrayBufferOrigins() {
+  std::string switch_value =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kSharedArrayBufferAllowedOrigins);
+  std::vector<std::string> list =
+      SplitString(switch_value, ",", base::WhitespaceHandling::TRIM_WHITESPACE,
+                  base::SplitResult::SPLIT_WANT_NONEMPTY);
+  std::vector<url::Origin> result;
+  for (auto& origin : list) {
+    GURL url(origin);
+    if (!url.is_valid() || url.scheme() != url::kHttpsScheme) {
+      LOG(FATAL) << "Invalid --" << switches::kSharedArrayBufferAllowedOrigins
+                 << " specified: " << switch_value;
+    }
+    result.push_back(url::Origin::Create(url));
+  }
+  return result;
+}
+}  // namespace
+#endif  // defined(OS_FUCHSIA)
+
+// static
+bool SecurityPolicy::IsSharedArrayBufferAlwaysAllowedForOrigin(
+    const SecurityOrigin* security_origin) {
+#if defined(OS_FUCHSIA)
+  static base::NoDestructor<std::vector<url::Origin>> allowed_origins(
+      GetSharedArrayBufferOrigins());
+  url::Origin origin = security_origin->ToUrlOrigin();
+  for (const url::Origin& allowed_origin : *allowed_origins) {
+    if (origin.scheme() == allowed_origin.scheme() &&
+        origin.DomainIs(allowed_origin.host()) &&
+        origin.port() == allowed_origin.port()) {
+      return true;
+    }
+  }
+#endif
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.h b/third_party/blink/renderer/platform/weborigin/security_policy.h
index 66a9ffa2..1a617c33 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.h
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -95,6 +95,9 @@
       const String& header_value,
       ReferrerPolicyLegacyKeywordsSupport,
       network::mojom::ReferrerPolicy* result);
+
+  static bool IsSharedArrayBufferAlwaysAllowedForOrigin(
+      const SecurityOrigin* origin);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index fbdcec20..a8d3029 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -1036,7 +1036,9 @@
             'third_party/blink/renderer/modules/mediasource/',
         ],
         'allowed': [
+            'base::CommandLine',
             'media::.+',
+            'switches::kLacrosEnablePlatformEncryptedHevc',
         ]
     },
     {
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/driver.py b/third_party/blink/tools/blinkpy/web_tests/port/driver.py
index c073a8f..00848e8 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/driver.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/driver.py
@@ -486,7 +486,8 @@
                 # so that the rest of the error-handling code can deal with
                 # this as if the test has simply crashed.
 
-        if self._port.get_option('initialize_webgpu_adapter_at_startup'):
+        if self._port.get_option(
+                'initialize_webgpu_adapter_at_startup_timeout_ms'):
             return self._initialize_webgpu_adapter_at_startup(per_test_args)
         return True, None
 
@@ -500,9 +501,11 @@
         # startup to be slower (if the workaround is triggered via the
         # --initialize-webgpu-adapter-at-startup flag, right above in the
         # code in _start()).
+        init_timeout = self._port.get_option(
+            'initialize_webgpu_adapter_at_startup_timeout_ms')
         startup_input = DriverInput(
             "wpt_internal/webgpu/000_run_me_first.html",
-            timeout=self._port.timeout_ms(),
+            timeout=init_timeout,
             image_hash=None,
             args=per_test_args)
         output = self._run_one_input(startup_input, start_time=time.time())
@@ -510,7 +513,7 @@
             return True, None
 
         output.text = ('Failed to initialize WebGPU adapter at startup '
-                       'via wpt_internal_webgpu/0000_run_me_first.html:\n' +
+                       'via wpt_internal_webgpu/000_run_me_first.html:\n' +
                        output.text)
         return False, output
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py b/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
index 2a28b8b..dd67be6 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py
@@ -551,8 +551,8 @@
             optparse.make_option('--time-out-ms',
                                  help='Set the timeout for each test'),
             optparse.make_option(
-                '--initialize-webgpu-adapter-at-startup',
-                action='store_true',
+                '--initialize-webgpu-adapter-at-startup-timeout-ms',
+                type='float',
                 help='Initialize WebGPU adapter before running any tests.'),
             optparse.make_option(
                 '--wrapper',
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 5db61fb..3728130 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1238,12 +1238,16 @@
 
 # The no-layout-ng bot compiles without proprietary codecs.
 # Timeout due to https://github.com/web-platform-tests/wpt/issues/29373
+crbug.com/591099 external/wpt/webcodecs/audio-decoder.https.any.html [ Failure ]
 crbug.com/591099 external/wpt/webcodecs/audioDecoder-codec-specific.any.html?adts_aac [ Failure ]
 crbug.com/591099 external/wpt/webcodecs/audioDecoder-codec-specific.any.html?mp4_aac [ Failure ]
+crbug.com/591099 external/wpt/webcodecs/video-decoder.https.any.html [ Failure ]
 crbug.com/591099 external/wpt/webcodecs/videoDecoder-codec-specific.any.html?h264_annexb [ Failure ]
 crbug.com/591099 external/wpt/webcodecs/videoDecoder-codec-specific.any.html?h264_avc [ Failure ]
+crbug.com/591099 external/wpt/webcodecs/audio-decoder.https.any.worker.html [ Failure Timeout ]
 crbug.com/591099 external/wpt/webcodecs/audioDecoder-codec-specific.any.worker.html?adts_aac [ Failure Timeout ]
 crbug.com/591099 external/wpt/webcodecs/audioDecoder-codec-specific.any.worker.html?mp4_aac [ Failure Timeout ]
+crbug.com/591099 external/wpt/webcodecs/video-decoder.https.any.worker.html [ Failure Timeout ]
 crbug.com/591099 external/wpt/webcodecs/videoDecoder-codec-specific.any.worker.html?h264_annexb [ Failure Timeout ]
 crbug.com/591099 external/wpt/webcodecs/videoDecoder-codec-specific.any.worker.html?h264_avc [ Failure Timeout ]
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic-expected.txt
index 8e4257e..715d9e2 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic-expected.txt
@@ -2,7 +2,7 @@
 FAIL setTimeout should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/imports-a.js?label=setTimeout"
 PASS eval should successfully import
 PASS Function should successfully import
-FAIL reflected-inline-event-handlers should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=reflected-inline-event-handlers"
-FAIL inline-event-handlers-UA-code should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=inline-event-handlers-UA-code"
+PASS reflected-inline-event-handlers should successfully import
+PASS inline-event-handlers-UA-code should successfully import
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module-expected.txt
index 8e4257e..715d9e2 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module-expected.txt
@@ -2,7 +2,7 @@
 FAIL setTimeout should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/imports-a.js?label=setTimeout"
 PASS eval should successfully import
 PASS Function should successfully import
-FAIL reflected-inline-event-handlers should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=reflected-inline-event-handlers"
-FAIL inline-event-handlers-UA-code should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=inline-event-handlers-UA-code"
+PASS reflected-inline-event-handlers should successfully import
+PASS inline-event-handlers-UA-code should successfully import
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic-expected.txt
index bb78d07..fe32f25 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic-expected.txt
@@ -2,7 +2,7 @@
 FAIL setTimeout should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/imports-a.js?label=setTimeout"
 PASS eval should successfully import
 PASS the Function constructor should successfully import
-FAIL reflected inline event handlers should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=reflected%20inline%20event%20handlers"
-FAIL inline event handlers triggered via UA code should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=inline%20event%20handlers%20triggered%20via%20UA%20code"
+PASS reflected inline event handlers should successfully import
+PASS inline event handlers triggered via UA code should successfully import
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module-expected.txt
index bb78d07..fe32f25 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module-expected.txt
@@ -2,7 +2,7 @@
 FAIL setTimeout should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/imports-a.js?label=setTimeout"
 PASS eval should successfully import
 PASS the Function constructor should successfully import
-FAIL reflected inline event handlers should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=reflected%20inline%20event%20handlers"
-FAIL inline event handlers triggered via UA code should successfully import promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch dynamically imported module: http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/module/dynamic-import/imports-a.js?label=inline%20event%20handlers%20triggered%20via%20UA%20code"
+PASS reflected inline event handlers should successfully import
+PASS inline event handlers triggered via UA code should successfully import
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-data.https.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/audio-data.https.any.js
new file mode 100644
index 0000000..a5cb478
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-data.https.any.js
@@ -0,0 +1,44 @@
+// META: global=window
+// META: script=/common/media.js
+// META: script=/webcodecs/utils.js
+
+var defaultInit = {
+  timestamp: 1234,
+  channels: 2,
+  sampleRate: 8000,
+  frames: 1,
+};
+
+function testAudioData(useView) {
+  let localData =
+      new SharedArrayBuffer(defaultInit.channels * defaultInit.frames * 4);
+  let view = new Float32Array(localData);
+  view[0] = -1.0;
+  view[1] = 1.0;
+
+  let audio_data_init = {
+    timestamp: defaultInit.timestamp,
+    data: useView ? view : localData,
+    numberOfFrames: defaultInit.frames,
+    numberOfChannels: defaultInit.channels,
+    sampleRate: defaultInit.sampleRate,
+    format: 'f32-planar',
+  }
+
+  let data = new AudioData(audio_data_init);
+
+  let copyDest = new SharedArrayBuffer(data.allocationSize({planeIndex: 0}));
+  let destView = new Float32Array(copyDest);
+  data.copyTo(useView ? destView : copyDest, {planeIndex: 0});
+  assert_equals(destView[0], -1.0, 'copyDest[0]');
+  data.copyTo(useView ? destView : copyDest, {planeIndex: 1});
+  assert_equals(destView[0], 1.0, 'copyDest[1]');
+}
+
+test(t => {
+  testAudioData(/*useView=*/ false);
+}, 'Test construction and copyTo() using a SharedArrayBuffer');
+
+test(t => {
+  testAudioData(/*useView=*/ true);
+}, 'Test construction and copyTo() using a Uint8Array(SharedArrayBuffer)');
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-data.https.any.js.headers b/third_party/blink/web_tests/external/wpt/webcodecs/audio-data.https.any.js.headers
new file mode 100644
index 0000000..5f8621ef8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-data.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.https.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.https.any.js
new file mode 100644
index 0000000..a13fb87
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.https.any.js
@@ -0,0 +1,70 @@
+// META: global=window,dedicatedworker
+// META: script=/webcodecs/utils.js
+
+const testData = {
+  src: 'sfx-aac.mp4',
+  config: {
+    codec: 'mp4a.40.2',
+    sampleRate: 48000,
+    numberOfChannels: 1,
+    description: {offset: 2552, size: 5},
+  }
+};
+
+// Create a view of an ArrayBuffer.
+function view(buffer, {offset, size}) {
+  return new Uint8Array(buffer, offset, size);
+}
+
+function testSharedArrayBufferDescription(t, useView) {
+  const data = testData;
+
+  // Don't run test if the codec is not supported.
+  let supported = false;
+  return AudioDecoder
+      .isConfigSupported({
+        codec: data.config.codec,
+        sampleRate: data.config.sampleRate,
+        numberOfChannels: data.config.numberOfChannels
+      })
+      .catch(_ => {
+        assert_implements_optional(false, data.config.codec + ' unsupported');
+      })
+      .then(support => {
+        supported = support.supported;
+        assert_implements_optional(
+            supported, data.config.codec + ' unsupported');
+        return fetch(data.src);
+      })
+      .then(response => {
+        return response.arrayBuffer();
+      })
+      .then(buf => {
+        config = {...data.config};
+        if (data.config.description) {
+          let desc = new SharedArrayBuffer(data.config.description.size);
+          let descView = new Uint8Array(desc);
+          descView.set(view(buf, data.config.description));
+          config.description = useView ? descView : desc;
+        }
+
+        // Support was verified above, so the description shouldn't change
+        // that.
+        return AudioDecoder.isConfigSupported(config);
+      })
+      .then(support => {
+        assert_true(support.supported);
+
+        const decoder = new AudioDecoder(getDefaultCodecInit(t));
+        decoder.configure(config);
+        assert_equals(decoder.state, 'configured', 'state');
+      });
+}
+
+promise_test(t => {
+  return testSharedArrayBufferDescription(t, /*useView=*/ false);
+}, 'Test isConfigSupported() and configure() using a SharedArrayBuffer');
+
+promise_test(t => {
+  return testSharedArrayBufferDescription(t, /*useView=*/ true);
+}, 'Test isConfigSupported() and configure() using a Uint8Array(SharedArrayBuffer)');
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.https.any.js.headers b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.https.any.js.headers
new file mode 100644
index 0000000..5f8621ef8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/encoded-audio-chunk.https.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-audio-chunk.https.any.js
new file mode 100644
index 0000000..7063d858
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-audio-chunk.https.any.js
@@ -0,0 +1,29 @@
+// META: global=window,dedicatedworker
+// META: script=/webcodecs/utils.js
+
+function testSharedArrayBufferEncodedAudioChunk(useView) {
+  let data = new SharedArrayBuffer(3);
+  let view = new Uint8Array(data);
+  view[0] = 0x0A;
+  view[1] = 0x0B;
+  view[2] = 0x0C;
+
+  let chunk = new EncodedAudioChunk(
+      {type: 'key', timestamp: 10, duration: 123, data: useView ? view : data});
+  assert_equals(chunk.byteLength, 3, 'byteLength');
+
+  let copyDest = new SharedArrayBuffer(3);
+  let destView = new Uint8Array(copyDest);
+  chunk.copyTo(useView ? destView : copyDest);
+  assert_equals(destView[0], 0x0A, 'copyDest[0]');
+  assert_equals(destView[1], 0x0B, 'copyDest[1]');
+  assert_equals(destView[2], 0x0C, 'copyDest[2]');
+}
+
+test(t => {
+  testSharedArrayBufferEncodedAudioChunk(/*useView=*/ false);
+}, 'Test construction and copyTo() using a SharedArrayBuffer');
+
+test(t => {
+  testSharedArrayBufferEncodedAudioChunk(/*useView=*/ true);
+}, 'Test construction and copyTo() using a Uint8Array(SharedArrayBuffer)');
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/encoded-audio-chunk.https.any.js.headers b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-audio-chunk.https.any.js.headers
new file mode 100644
index 0000000..5f8621ef8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-audio-chunk.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/encoded-video-chunk.https.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-video-chunk.https.any.js
new file mode 100644
index 0000000..7f414fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-video-chunk.https.any.js
@@ -0,0 +1,29 @@
+// META: global=window,dedicatedworker
+// META: script=/webcodecs/utils.js
+
+function testSharedArrayBufferEncodedVideoChunk(useView) {
+  let data = new SharedArrayBuffer(3);
+  let view = new Uint8Array(data);
+  view[0] = 0x0A;
+  view[1] = 0x0B;
+  view[2] = 0x0C;
+
+  let chunk = new EncodedVideoChunk(
+      {type: 'key', timestamp: 10, duration: 123, data: useView ? view : data});
+  assert_equals(chunk.byteLength, 3, 'byteLength');
+
+  let copyDest = new SharedArrayBuffer(3);
+  let destView = new Uint8Array(copyDest);
+  chunk.copyTo(useView ? destView : copyDest);
+  assert_equals(destView[0], 0x0A, 'copyDest[0]');
+  assert_equals(destView[1], 0x0B, 'copyDest[1]');
+  assert_equals(destView[2], 0x0C, 'copyDest[2]');
+}
+
+test(t => {
+  testSharedArrayBufferEncodedVideoChunk(/*useView=*/ false);
+}, 'Test construction and copyTo() using a SharedArrayBuffer');
+
+test(t => {
+  testSharedArrayBufferEncodedVideoChunk(/*useView=*/ true);
+}, 'Test construction and copyTo() using a Uint8Array(SharedArrayBuffer)');
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/encoded-video-chunk.https.any.js.headers b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-video-chunk.https.any.js.headers
new file mode 100644
index 0000000..5f8621ef8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/encoded-video-chunk.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.https.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.https.any.js
new file mode 100644
index 0000000..14777c55
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.https.any.js
@@ -0,0 +1,67 @@
+// META: global=window,dedicatedworker
+// META: script=/webcodecs/utils.js
+
+const testData = {
+  src: 'h264.mp4',
+  config: {
+    codec: 'avc1.64000b',
+    description: {offset: 9490, size: 45},
+    codedWidth: 320,
+    codedHeight: 240,
+    displayAspectWidth: 320,
+    displayAspectHeight: 240,
+  }
+};
+
+// Create a view of an ArrayBuffer.
+function view(buffer, {offset, size}) {
+  return new Uint8Array(buffer, offset, size);
+}
+
+function testSharedArrayBufferDescription(t, useView) {
+  const data = testData;
+
+  // Don't run test if the codec is not supported.
+  let supported = false;
+  return VideoDecoder.isConfigSupported({codec: data.config.codec})
+      .catch(_ => {
+        assert_implements_optional(false, data.config.codec + ' unsupported');
+      })
+      .then(support => {
+        supported = support.supported;
+        assert_implements_optional(
+            supported, data.config.codec + ' unsupported');
+        return fetch(data.src);
+      })
+      .then(response => {
+        return response.arrayBuffer();
+      })
+      .then(buf => {
+        config = {...data.config};
+        if (data.config.description) {
+          let desc = new SharedArrayBuffer(data.config.description.size);
+          let descView = new Uint8Array(desc);
+          descView.set(view(buf, data.config.description));
+          config.description = useView ? descView : desc;
+        }
+
+        // Support was verified above, so the description shouldn't change
+        // that.
+        return VideoDecoder.isConfigSupported(config);
+      })
+      .then(support => {
+        assert_true(support.supported);
+
+        const decoder = new VideoDecoder(getDefaultCodecInit(t));
+        decoder.configure(config);
+        assert_equals(decoder.state, 'configured', 'state');
+      });
+}
+
+promise_test(t => {
+  return testSharedArrayBufferDescription(t, /*useView=*/ false);
+}, 'Test isConfigSupported() and configure() using a SharedArrayBuffer');
+
+promise_test(t => {
+  return testSharedArrayBufferDescription(t, /*useView=*/ true);
+}, 'Test isConfigSupported() and configure() using a Uint8Array(SharedArrayBuffer)');
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.https.any.js.headers b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.https.any.js.headers
new file mode 100644
index 0000000..5f8621ef8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/third_party/blink/web_tests/http/tests/media/media-controls-live-timeline.html b/third_party/blink/web_tests/http/tests/media/media-controls-live-timeline.html
new file mode 100644
index 0000000..7ee055b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/media/media-controls-live-timeline.html
@@ -0,0 +1,46 @@
+<title>Test timeline work properly when playing live video.</title>
+<script src="/w3c/resources/testharness.js"></script>
+<script src="/w3c/resources/testharnessreport.js"></script>
+<script src="media-source/mediasource-util.js"></script>
+<script src="../../media-resources/media-controls.js"></script>
+<video controls></video>
+<script>
+  const epsilon = 0.2;
+  const pause_delay = 1;  // 1 second.
+
+  mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData){
+    mediaElement.play();
+    mediaSource.duration = +Infinity;
+    let timeline = timelineElement(mediaElement);
+
+    // Append all media data for complete playback.
+    test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer end update.');
+    test.expectEvent(mediaElement, 'loadedmetadata', 'Reached HAVE_METADATA');
+    test.expectEvent(mediaElement, 'playing', 'Playing media.');
+    sourceBuffer.appendBuffer(mediaData);
+
+    test.waitForExpectedEvents(function() {
+      test.waitForCurrentTimeChange(mediaElement, function() {
+        assert_approx_equals(Number(timeline.value), Number(timeline.max),
+                             epsilon, "timline thumb should be near the end");
+
+        mediaElement.onpause = test.step_func(function() {
+          // Timeline value should be at least 1s away from end at some point
+          // after paused.
+          function waitForTimeline() {
+            if (Number(timeline.max) - Number(timeline.value) >= pause_delay) {
+              test.done();
+              return;
+            }
+            test.step_timeout(waitForTimeline, 1000);
+          }
+
+          waitForTimeline();
+        });
+
+        mediaElement.pause();
+      });
+    });
+  }, "Timeline work properly when playing live video");
+
+</script>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt
index 762c7838..00d227d 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 633 tests; 376 PASS, 257 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 659 tests; 377 PASS, 282 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -297,7 +297,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 FAIL Parsing: <http://[google.com]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[google.com]" but got "http://[google.com]/"
 FAIL Parsing: <http://[::1.2.3.4x]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[::1.2.3.4x]" but got "http://[::1.2.3.4x]/"
@@ -421,11 +420,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 FAIL Parsing: <http://10000000000> against <http://other.com/> assert_equals: failure should set href to input expected "http://10000000000" but got "http://10000000000/"
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -434,7 +437,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 FAIL Parsing: <http://0xffffffff1> against <http://other.com/> assert_equals: failure should set href to input expected "http://0xffffffff1" but got "http://0xffffffff1/"
 FAIL Parsing: <http://256.256.256.256> against <http://other.com/> assert_equals: failure should set href to input expected "http://256.256.256.256" but got "http://256.256.256.256/"
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -589,7 +591,6 @@
 FAIL Parsing: <non-special://[:80/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 FAIL Parsing: <http://[::127.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "http://[::127.0.0.0.1]" but got "http://[::127.0.0.0.1]/"
@@ -644,5 +645,30 @@
 FAIL Parsing: <abc:rootless> against <abc:/path> assert_equals: href expected "abc:rootless" but got "abc:/rootless"
 PASS Parsing: <abc:rootless> against <abc:path>
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0x100.2.3.4> against <about:blank> assert_equals: failure should set href to input expected "http://0x100.2.3.4" but got "http://0x100.2.3.4/"
+FAIL Parsing: <http://0x100.2.3.4.> against <about:blank> assert_equals: failure should set href to input expected "http://0x100.2.3.4." but got "http://0x100.2.3.4./"
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.09> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/failure-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/failure-expected.txt
index 21264a7..e5fd1f4 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/failure-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/failure-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 485 tests; 265 PASS, 220 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 611 tests; 275 PASS, 336 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL's constructor's base argument: file://example:1/ should throw
 PASS URL's href: file://example:1/ should throw
@@ -485,5 +485,131 @@
 PASS sendBeacon(): file://xn--/p should throw
 FAIL Location's href: file://xn--/p should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
 FAIL window.open(): file://xn--/p should throw assert_throws_dom: function "() => self.open(test.input).close()" threw object "TypeError: Cannot read properties of null (reading 'close')" that is not a DOMException SyntaxError: property "code" is equal to undefined, expected 12
+FAIL URL's constructor's base argument: http://0..0x300/ should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0..0x300/ should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0..0x300/ should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0..0x300/ should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0..0x300/ should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0..0x300/ should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://0..0x300./ should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0..0x300./ should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0..0x300./ should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0..0x300./ should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0..0x300./ should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0..0x300./ should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.08 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.08 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.08 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.08 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.08 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.08 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.08. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.08. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.08. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.08. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.08. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.08. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.09 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.09 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.09 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.09 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.09 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.09 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://09.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://09.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://09.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://09.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://09.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://09.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://09.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://09.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://09.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://09.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://09.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://09.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://01.2.3.4.5 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://01.2.3.4.5 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://01.2.3.4.5 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://01.2.3.4.5 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://01.2.3.4.5 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://01.2.3.4.5 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://01.2.3.4.5. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://01.2.3.4.5. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://01.2.3.4.5. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://01.2.3.4.5. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://01.2.3.4.5. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://01.2.3.4.5. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+PASS URL's constructor's base argument: http://0x100.2.3.4 should throw
+PASS URL's href: http://0x100.2.3.4 should throw
+PASS XHR: http://0x100.2.3.4 should throw
+PASS sendBeacon(): http://0x100.2.3.4 should throw
+FAIL Location's href: http://0x100.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" threw object "SyntaxError: Failed to set the 'href' property on 'Location': 'http://0x100.2.3.4' is not a valid URL." ("SyntaxError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+PASS window.open(): http://0x100.2.3.4 should throw
+PASS URL's constructor's base argument: http://0x100.2.3.4. should throw
+PASS URL's href: http://0x100.2.3.4. should throw
+PASS XHR: http://0x100.2.3.4. should throw
+PASS sendBeacon(): http://0x100.2.3.4. should throw
+FAIL Location's href: http://0x100.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" threw object "SyntaxError: Failed to set the 'href' property on 'Location': 'http://0x100.2.3.4.' is not a valid URL." ("SyntaxError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+PASS window.open(): http://0x100.2.3.4. should throw
+FAIL URL's constructor's base argument: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0x1.2.3.4.5 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0x1.2.3.4.5 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0x1.2.3.4.5 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0x1.2.3.4.5. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0x1.2.3.4.5. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0x1.2.3.4.5. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.1.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.1.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.1.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.1.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.1.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.1.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.1.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.1.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.1.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.1.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.1.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.1.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.09 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.09 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.09 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.09 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.09 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.09 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.09. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.09. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.09. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.09. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.09. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.09. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.0x4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.0x4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.0x4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.0x4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.0x4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.0x4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.0x4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.0x4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.0x4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.0x4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.0x4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.0x4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any-expected.txt
index eb20797..e931b952 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 452 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 661 tests; 455 PASS, 206 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -317,7 +317,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 PASS Parsing: <http://[google.com]> against <http://other.com/>
 PASS Parsing: <http://[::1.2.3.4x]> against <http://other.com/>
@@ -491,11 +490,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 PASS Parsing: <http://10000000000> against <http://other.com/>
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -504,7 +507,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 PASS Parsing: <http://0xffffffff1> against <http://other.com/>
 PASS Parsing: <http://256.256.256.256> against <http://other.com/>
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -673,7 +675,6 @@
         }" did not throw
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 PASS Parsing: <http://[::127.0.0.0.1]> against <about:blank>
@@ -732,5 +733,76 @@
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
 PASS Parsing: <#> against <null>
 PASS Parsing: <?> against <null>
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+PASS Parsing: <http://0x100.2.3.4> against <about:blank>
+PASS Parsing: <http://0x100.2.3.4.> against <about:blank>
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any.worker-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any.worker-expected.txt
index eb20797..e931b952 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any.worker-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 452 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 661 tests; 455 PASS, 206 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -317,7 +317,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 PASS Parsing: <http://[google.com]> against <http://other.com/>
 PASS Parsing: <http://[::1.2.3.4x]> against <http://other.com/>
@@ -491,11 +490,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 PASS Parsing: <http://10000000000> against <http://other.com/>
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -504,7 +507,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 PASS Parsing: <http://0xffffffff1> against <http://other.com/>
 PASS Parsing: <http://256.256.256.256> against <http://other.com/>
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -673,7 +675,6 @@
         }" did not throw
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 PASS Parsing: <http://[::127.0.0.0.1]> against <about:blank>
@@ -732,5 +733,76 @@
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
 PASS Parsing: <#> against <null>
 PASS Parsing: <?> against <null>
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+PASS Parsing: <http://0x100.2.3.4> against <about:blank>
+PASS Parsing: <http://0x100.2.3.4.> against <about:blank>
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-expected.txt
deleted file mode 100644
index b80c6751..0000000
--- a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-expected.txt
+++ /dev/null
@@ -1,648 +0,0 @@
-This is a testharness.js-based test.
-Found 633 tests; 376 PASS, 257 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Loading data…
-PASS Parsing: <http://example	.
-org> against <http://example.org/foo/bar>
-PASS Parsing: <http://user:pass@foo:21/bar;par?b#c> against <http://example.org/foo/bar>
-PASS Parsing: <https://test:@test> against <about:blank>
-PASS Parsing: <https://:@test> against <about:blank>
-FAIL Parsing: <non-special://test:@test/x> against <about:blank> assert_equals: href expected "non-special://test@test/x" but got "non-special://test:@test/x"
-FAIL Parsing: <non-special://:@test/x> against <about:blank> assert_equals: href expected "non-special://test/x" but got "non-special://:@test/x"
-PASS Parsing: <http:foo.com> against <http://example.org/foo/bar>
-PASS Parsing: <	   :foo.com   
-> against <http://example.org/foo/bar>
-PASS Parsing: < foo.com  > against <http://example.org/foo/bar>
-PASS Parsing: <a:	 foo.com> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
-PASS Parsing: <lolscheme:x x#x x> against <about:blank>
-PASS Parsing: <http://f:/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:0/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:b/c> against <http://example.org/foo/bar>
-FAIL Parsing: <http://f: /c> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://f: /c" but got "http://f:%20/c"
-PASS Parsing: <http://f:
-/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:fifty-two/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:999999/c> against <http://example.org/foo/bar>
-FAIL Parsing: <non-special://f:999999/c> against <http://example.org/foo/bar> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://f: 21 / b ? d # e > against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://f: 21 / b ? d # e " but got "http://f:%2021%20/%20b%20?%20d%20#%20e"
-PASS Parsing: <> against <http://example.org/foo/bar>
-PASS Parsing: <  	> against <http://example.org/foo/bar>
-PASS Parsing: <:foo.com/> against <http://example.org/foo/bar>
-PASS Parsing: <:foo.com\> against <http://example.org/foo/bar>
-PASS Parsing: <:> against <http://example.org/foo/bar>
-PASS Parsing: <:a> against <http://example.org/foo/bar>
-PASS Parsing: <:/> against <http://example.org/foo/bar>
-PASS Parsing: <:\> against <http://example.org/foo/bar>
-PASS Parsing: <:#> against <http://example.org/foo/bar>
-PASS Parsing: <#> against <http://example.org/foo/bar>
-PASS Parsing: <#/> against <http://example.org/foo/bar>
-PASS Parsing: <#\> against <http://example.org/foo/bar>
-PASS Parsing: <#;?> against <http://example.org/foo/bar>
-PASS Parsing: <?> against <http://example.org/foo/bar>
-PASS Parsing: </> against <http://example.org/foo/bar>
-PASS Parsing: <:23> against <http://example.org/foo/bar>
-PASS Parsing: </:23> against <http://example.org/foo/bar>
-PASS Parsing: <\x> against <http://example.org/foo/bar>
-PASS Parsing: <\\x\hello> against <http://example.org/foo/bar>
-PASS Parsing: <::> against <http://example.org/foo/bar>
-PASS Parsing: <::23> against <http://example.org/foo/bar>
-FAIL Parsing: <foo://> against <http://example.org/foo/bar> assert_equals: pathname expected "" but got "//"
-PASS Parsing: <http://a:b@c:29/d> against <http://example.org/foo/bar>
-PASS Parsing: <http::@c:29> against <http://example.org/foo/bar>
-PASS Parsing: <http://&a:foo(b]c@d:2/> against <http://example.org/foo/bar>
-PASS Parsing: <http://::@c@d:2> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo.com/\@> against <http://example.org/foo/bar>
-PASS Parsing: <http:\\foo.com\> against <http://example.org/foo/bar>
-PASS Parsing: <http:\\a\b:c\d@foo.com\> against <http://example.org/foo/bar>
-PASS Parsing: <foo:/> against <http://example.org/foo/bar>
-PASS Parsing: <foo:/bar.com/> against <http://example.org/foo/bar>
-FAIL Parsing: <foo://///////> against <http://example.org/foo/bar> assert_equals: pathname expected "///////" but got "/////////"
-FAIL Parsing: <foo://///////bar.com/> against <http://example.org/foo/bar> assert_equals: pathname expected "///////bar.com/" but got "/////////bar.com/"
-FAIL Parsing: <foo:////://///> against <http://example.org/foo/bar> assert_equals: pathname expected "//://///" but got "////://///"
-PASS Parsing: <c:/foo> against <http://example.org/foo/bar>
-PASS Parsing: <//foo/bar> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo/path;a??e#f#g> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo/abcd?efgh?ijkl> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo/abcd#foo?bar> against <http://example.org/foo/bar>
-PASS Parsing: <[61:24:74]:98> against <http://example.org/foo/bar>
-PASS Parsing: <http:[61:27]/:foo> against <http://example.org/foo/bar>
-FAIL Parsing: <http://[1::2]:3:4> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://[1::2]:3:4" but got "http://[1::2]:3:4/"
-FAIL Parsing: <http://2001::1> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://2001::1" but got "http://2001::1/"
-FAIL Parsing: <http://2001::1]> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://2001::1]" but got "http://2001::1]/"
-FAIL Parsing: <http://2001::1]:80> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://2001::1]:80" but got "http://2001::1]/"
-PASS Parsing: <http://[2001::1]> against <http://example.org/foo/bar>
-PASS Parsing: <http://[::127.0.0.1]> against <http://example.org/foo/bar>
-PASS Parsing: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>
-PASS Parsing: <http://[2001::1]:80> against <http://example.org/foo/bar>
-PASS Parsing: <http:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ftp:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <https:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <madeupscheme:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <file:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <file://example:1/> against <about:blank>
-PASS Parsing: <file://example:test/> against <about:blank>
-FAIL Parsing: <file://example%/> against <about:blank> assert_equals: failure should set href to input expected "file://example%/" but got "file://example%25/"
-PASS Parsing: <file://[example]/> against <about:blank>
-PASS Parsing: <ftps:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <gopher:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ws:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <wss:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <data:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <javascript:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <mailto:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <http:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ftp:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <https:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <madeupscheme:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ftps:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <gopher:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ws:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <wss:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <data:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <javascript:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <mailto:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: </a/b/c> against <http://example.org/foo/bar>
-PASS Parsing: </a/ /c> against <http://example.org/foo/bar>
-PASS Parsing: </a%2fc> against <http://example.org/foo/bar>
-PASS Parsing: </a/%2f/c> against <http://example.org/foo/bar>
-PASS Parsing: <#β> against <http://example.org/foo/bar>
-PASS Parsing: <data:text/html,test#test> against <http://example.org/foo/bar>
-PASS Parsing: <tel:1234567890> against <http://example.org/foo/bar>
-FAIL Parsing: <ssh://example.com/foo/bar.git> against <http://example.org/> assert_equals: host expected "example.com" but got ""
-FAIL Parsing: <file:c:\foo\bar.html> against <file:///tmp/mock/path> assert_equals: href expected "file:///c:/foo/bar.html" but got "file:///tmp/mock/c:/foo/bar.html"
-FAIL Parsing: <  File:c|////foo\bar.html> against <file:///tmp/mock/path> assert_equals: href expected "file:///c:////foo/bar.html" but got "file:///tmp/mock/c%7C////foo/bar.html"
-FAIL Parsing: <C|/foo/bar> against <file:///tmp/mock/path> assert_equals: href expected "file:///C:/foo/bar" but got "file:///tmp/mock/C%7C/foo/bar"
-FAIL Parsing: </C|\foo\bar> against <file:///tmp/mock/path> assert_equals: href expected "file:///C:/foo/bar" but got "file:///C%7C/foo/bar"
-FAIL Parsing: <//C|/foo/bar> against <file:///tmp/mock/path> assert_equals: href expected "file:///C:/foo/bar" but got "file://c%7C/foo/bar"
-PASS Parsing: <//server/file> against <file:///tmp/mock/path>
-PASS Parsing: <\\server\file> against <file:///tmp/mock/path>
-PASS Parsing: </\server/file> against <file:///tmp/mock/path>
-PASS Parsing: <file:///foo/bar.txt> against <file:///tmp/mock/path>
-PASS Parsing: <file:///home/me> against <file:///tmp/mock/path>
-PASS Parsing: <//> against <file:///tmp/mock/path>
-PASS Parsing: <///> against <file:///tmp/mock/path>
-PASS Parsing: <///test> against <file:///tmp/mock/path>
-PASS Parsing: <file://test> against <file:///tmp/mock/path>
-FAIL Parsing: <file://localhost> against <file:///tmp/mock/path> assert_equals: href expected "file:///" but got "file://localhost/"
-FAIL Parsing: <file://localhost/> against <file:///tmp/mock/path> assert_equals: href expected "file:///" but got "file://localhost/"
-FAIL Parsing: <file://localhost/test> against <file:///tmp/mock/path> assert_equals: href expected "file:///test" but got "file://localhost/test"
-PASS Parsing: <test> against <file:///tmp/mock/path>
-PASS Parsing: <file:test> against <file:///tmp/mock/path>
-PASS Parsing: <http://example.com/././foo> against <about:blank>
-PASS Parsing: <http://example.com/./.foo> against <about:blank>
-PASS Parsing: <http://example.com/foo/.> against <about:blank>
-PASS Parsing: <http://example.com/foo/./> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/..> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/../> against <about:blank>
-PASS Parsing: <http://example.com/foo/..bar> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/../ton> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/../ton/../../a> against <about:blank>
-PASS Parsing: <http://example.com/foo/../../..> against <about:blank>
-PASS Parsing: <http://example.com/foo/../../../ton> against <about:blank>
-PASS Parsing: <http://example.com/foo/%2e> against <about:blank>
-FAIL Parsing: <http://example.com/foo/%2e%2> against <about:blank> assert_equals: href expected "http://example.com/foo/%2e%2" but got "http://example.com/foo/.%2"
-FAIL Parsing: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank> assert_equals: href expected "http://example.com/%2e.bar" but got "http://example.com/..bar"
-PASS Parsing: <http://example.com////../..> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar//../..> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar//..> against <about:blank>
-PASS Parsing: <http://example.com/foo> against <about:blank>
-PASS Parsing: <http://example.com/%20foo> against <about:blank>
-PASS Parsing: <http://example.com/foo%> against <about:blank>
-PASS Parsing: <http://example.com/foo%2> against <about:blank>
-PASS Parsing: <http://example.com/foo%2zbar> against <about:blank>
-PASS Parsing: <http://example.com/foo%2©zbar> against <about:blank>
-FAIL Parsing: <http://example.com/foo%41%7a> against <about:blank> assert_equals: href expected "http://example.com/foo%41%7a" but got "http://example.com/fooAz"
-PASS Parsing: <http://example.com/foo	‘%91> against <about:blank>
-FAIL Parsing: <http://example.com/foo%00%51> against <about:blank> assert_equals: href expected "http://example.com/foo%00%51" but got "http://example.com/foo%00Q"
-PASS Parsing: <http://example.com/(%28:%3A%29)> against <about:blank>
-PASS Parsing: <http://example.com/%3A%3a%3C%3c> against <about:blank>
-PASS Parsing: <http://example.com/foo	bar> against <about:blank>
-PASS Parsing: <http://example.com\\foo\\bar> against <about:blank>
-PASS Parsing: <http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd> against <about:blank>
-PASS Parsing: <http://example.com/@asdf%40> against <about:blank>
-PASS Parsing: <http://example.com/你好你好> against <about:blank>
-PASS Parsing: <http://example.com/‥/foo> against <about:blank>
-PASS Parsing: <http://example.com//foo> against <about:blank>
-PASS Parsing: <http://example.com/‮/foo/‭/bar> against <about:blank>
-PASS Parsing: <http://www.google.com/foo?bar=baz#> against <about:blank>
-PASS Parsing: <http://www.google.com/foo?bar=baz# »> against <about:blank>
-PASS Parsing: <data:test# »> against <about:blank>
-PASS Parsing: <http://www.google.com> against <about:blank>
-PASS Parsing: <http://192.0x00A80001> against <about:blank>
-FAIL Parsing: <http://www/foo%2Ehtml> against <about:blank> assert_equals: href expected "http://www/foo%2Ehtml" but got "http://www/foo.html"
-PASS Parsing: <http://www/foo/%2E/html> against <about:blank>
-PASS Parsing: <http://user:pass@/> against <about:blank>
-PASS Parsing: <http://%25DOMAIN:foobar@foodomain.com/> against <about:blank>
-PASS Parsing: <http:\\www.google.com\foo> against <about:blank>
-PASS Parsing: <http://foo:80/> against <about:blank>
-PASS Parsing: <http://foo:81/> against <about:blank>
-FAIL Parsing: <httpa://foo:80/> against <about:blank> assert_equals: host expected "foo:80" but got ""
-PASS Parsing: <http://foo:-80/> against <about:blank>
-PASS Parsing: <https://foo:443/> against <about:blank>
-PASS Parsing: <https://foo:80/> against <about:blank>
-PASS Parsing: <ftp://foo:21/> against <about:blank>
-PASS Parsing: <ftp://foo:80/> against <about:blank>
-FAIL Parsing: <gopher://foo:70/> against <about:blank> assert_equals: host expected "foo:70" but got ""
-FAIL Parsing: <gopher://foo:443/> against <about:blank> assert_equals: host expected "foo:443" but got ""
-PASS Parsing: <ws://foo:80/> against <about:blank>
-PASS Parsing: <ws://foo:81/> against <about:blank>
-PASS Parsing: <ws://foo:443/> against <about:blank>
-PASS Parsing: <ws://foo:815/> against <about:blank>
-PASS Parsing: <wss://foo:80/> against <about:blank>
-PASS Parsing: <wss://foo:81/> against <about:blank>
-PASS Parsing: <wss://foo:443/> against <about:blank>
-PASS Parsing: <wss://foo:815/> against <about:blank>
-PASS Parsing: <http:/example.com/> against <about:blank>
-PASS Parsing: <ftp:/example.com/> against <about:blank>
-PASS Parsing: <https:/example.com/> against <about:blank>
-PASS Parsing: <madeupscheme:/example.com/> against <about:blank>
-PASS Parsing: <file:/example.com/> against <about:blank>
-PASS Parsing: <ftps:/example.com/> against <about:blank>
-PASS Parsing: <gopher:/example.com/> against <about:blank>
-PASS Parsing: <ws:/example.com/> against <about:blank>
-PASS Parsing: <wss:/example.com/> against <about:blank>
-PASS Parsing: <data:/example.com/> against <about:blank>
-PASS Parsing: <javascript:/example.com/> against <about:blank>
-PASS Parsing: <mailto:/example.com/> against <about:blank>
-PASS Parsing: <http:example.com/> against <about:blank>
-PASS Parsing: <ftp:example.com/> against <about:blank>
-PASS Parsing: <https:example.com/> against <about:blank>
-PASS Parsing: <madeupscheme:example.com/> against <about:blank>
-PASS Parsing: <ftps:example.com/> against <about:blank>
-PASS Parsing: <gopher:example.com/> against <about:blank>
-PASS Parsing: <ws:example.com/> against <about:blank>
-PASS Parsing: <wss:example.com/> against <about:blank>
-PASS Parsing: <data:example.com/> against <about:blank>
-PASS Parsing: <javascript:example.com/> against <about:blank>
-PASS Parsing: <mailto:example.com/> against <about:blank>
-PASS Parsing: <http:@www.example.com> against <about:blank>
-PASS Parsing: <http:/@www.example.com> against <about:blank>
-PASS Parsing: <http://@www.example.com> against <about:blank>
-PASS Parsing: <http:a:b@www.example.com> against <about:blank>
-PASS Parsing: <http:/a:b@www.example.com> against <about:blank>
-PASS Parsing: <http://a:b@www.example.com> against <about:blank>
-PASS Parsing: <http://@pple.com> against <about:blank>
-PASS Parsing: <http::b@www.example.com> against <about:blank>
-PASS Parsing: <http:/:b@www.example.com> against <about:blank>
-PASS Parsing: <http://:b@www.example.com> against <about:blank>
-FAIL Parsing: <http:/:@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/:@/www.example.com" but got "http:///www.example.com"
-PASS Parsing: <http://user@/www.example.com> against <about:blank>
-FAIL Parsing: <http:@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:@/www.example.com" but got "http:///www.example.com"
-FAIL Parsing: <http:/@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/@/www.example.com" but got "http:///www.example.com"
-FAIL Parsing: <http://@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http://@/www.example.com" but got "http:///www.example.com"
-FAIL Parsing: <https:@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "https:@/www.example.com" but got "https:///www.example.com"
-FAIL Parsing: <http:a:b@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:a:b@/www.example.com" but got "http://a:b@/www.example.com"
-FAIL Parsing: <http:/a:b@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/a:b@/www.example.com" but got "http://a:b@/www.example.com"
-PASS Parsing: <http://a:b@/www.example.com> against <about:blank>
-FAIL Parsing: <http::@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http::@/www.example.com" but got "http:///www.example.com"
-PASS Parsing: <http:a:@www.example.com> against <about:blank>
-PASS Parsing: <http:/a:@www.example.com> against <about:blank>
-PASS Parsing: <http://a:@www.example.com> against <about:blank>
-PASS Parsing: <http://www.@pple.com> against <about:blank>
-FAIL Parsing: <http:@:www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:@:www.example.com" but got "http://:www.example.com/"
-FAIL Parsing: <http:/@:www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/@:www.example.com" but got "http://:www.example.com/"
-FAIL Parsing: <http://@:www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http://@:www.example.com" but got "http://:www.example.com/"
-PASS Parsing: <http://:@www.example.com> against <about:blank>
-PASS Parsing: </> against <http://www.example.com/test>
-PASS Parsing: </test.txt> against <http://www.example.com/test>
-PASS Parsing: <.> against <http://www.example.com/test>
-PASS Parsing: <..> against <http://www.example.com/test>
-PASS Parsing: <test.txt> against <http://www.example.com/test>
-PASS Parsing: <./test.txt> against <http://www.example.com/test>
-PASS Parsing: <../test.txt> against <http://www.example.com/test>
-PASS Parsing: <../aaa/test.txt> against <http://www.example.com/test>
-PASS Parsing: <../../test.txt> against <http://www.example.com/test>
-PASS Parsing: <中/test.txt> against <http://www.example.com/test>
-PASS Parsing: <http://www.example2.com> against <http://www.example.com/test>
-PASS Parsing: <//www.example2.com> against <http://www.example.com/test>
-PASS Parsing: <file:...> against <http://www.example.com/test>
-PASS Parsing: <file:..> against <http://www.example.com/test>
-PASS Parsing: <file:a> against <http://www.example.com/test>
-PASS Parsing: <http://ExAmPlE.CoM> against <http://other.com/>
-FAIL Parsing: <http://example example.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://Goo%20 goo%7C|.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://[]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[]" but got "http://[]/"
-FAIL Parsing: <http://[:]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[:]" but got "http://[:]/"
-FAIL Parsing: <http://GOO  goo.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://GOO​⁠goo.com> against <http://other.com/>
-PASS Parsing: <\0 http://example.com/ \r > against <about:blank>
-PASS Parsing: <http://www.foo。bar.com> against <http://other.com/>
-FAIL Parsing: <http://﷐zyx.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://﷐zyx.com" but got "http://%EF%BF%BDzyx.com/"
-FAIL Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%ef%b7%90zyx.com" but got "http://%EF%BF%BDzyx.com/"
-FAIL Parsing: <https://�> against <about:blank> assert_equals: failure should set href to input expected "https://\ufffd" but got "https://%EF%BF%BD/"
-FAIL Parsing: <https://%EF%BF%BD> against <about:blank> assert_equals: failure should set href to input expected "https://%EF%BF%BD" but got "https://%EF%BF%BD/"
-PASS Parsing: <https://x/�?�#�> against <about:blank>
-FAIL Parsing: <http://a.b.c.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://Go.com> against <http://other.com/>
-FAIL Parsing: <http://%41.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://%ef%bc%85%ef%bc%94%ef%bc%91.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://%00.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%00.com" but got "http://%00.com/"
-FAIL Parsing: <http://%ef%bc%85%ef%bc%90%ef%bc%90.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%ef%bc%85%ef%bc%90%ef%bc%90.com" but got "http://%00.com/"
-PASS Parsing: <http://你好你好> against <http://other.com/>
-FAIL Parsing: <https://faß.ExAmPlE/> against <about:blank> assert_equals: href expected "https://xn--fa-hia.example/" but got "https://fass.example/"
-FAIL Parsing: <sc://faß.ExAmPlE/> against <about:blank> assert_equals: host expected "fa%C3%9F.ExAmPlE" but got ""
-FAIL Parsing: <http://%zz%66%a.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%zz%66%a.com" but got "http://%25zzf%25a.com/"
-FAIL Parsing: <http://%25> against <http://other.com/> assert_equals: failure should set href to input expected "http://%25" but got "http://%25/"
-FAIL Parsing: <http://hello%00> against <http://other.com/> assert_equals: failure should set href to input expected "http://hello%00" but got "http://hello%00/"
-PASS Parsing: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>
-PASS Parsing: <http://%30%78%63%30%2e%30%32%35%30.01%2e> against <http://other.com/>
-FAIL Parsing: <http://192.168.0.257> against <http://other.com/> assert_equals: failure should set href to input expected "http://192.168.0.257" but got "http://192.168.0.257/"
-FAIL Parsing: <http://%3g%78%63%30%2e%30%32%35%30%2E.01> against <http://other.com/> assert_equals: failure should set href to input expected "http://%3g%78%63%30%2e%30%32%35%30%2E.01" but got "http://%253gxc0.0250..01/"
-FAIL Parsing: <http://192.168.0.1 hello> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <https://x x:12> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
-PASS Parsing: <http://./> against <about:blank>
-PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
-PASS Parsing: <http://[www.google.com]/> against <about:blank>
-FAIL Parsing: <http://[google.com]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[google.com]" but got "http://[google.com]/"
-FAIL Parsing: <http://[::1.2.3.4x]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[::1.2.3.4x]" but got "http://[::1.2.3.4x]/"
-FAIL Parsing: <http://[::1.2.3.]> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://[::1.2.]> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://[::1.]> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://foo:💩@example.com/bar> against <http://other.com/>
-PASS Parsing: <#> against <test:test>
-PASS Parsing: <#x> against <mailto:x@x.com>
-FAIL Parsing: <#x> against <data:,> assert_equals: href expected "data:,#x" but got "mailto:x@x.com#x"
-PASS Parsing: <#x> against <about:blank>
-PASS Parsing: <#> against <test:test?test>
-PASS Parsing: <https://@test@test@example:800/> against <http://doesnotmatter/>
-PASS Parsing: <https://@@@example> against <http://doesnotmatter/>
-PASS Parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>
-PASS Parsing: <http://host/?'> against <about:blank>
-FAIL Parsing: <notspecial://host/?'> against <about:blank> assert_equals: host expected "host" but got ""
-PASS Parsing: </some/path> against <http://user@example.org/smth>
-PASS Parsing: <> against <http://user:pass@example.org:21/smth>
-PASS Parsing: </some/path> against <http://user:pass@example.org:21/smth>
-FAIL Parsing: <i> against <sc:sd> assert_equals: failure should set href to input expected "i" but got ""
-FAIL Parsing: <i> against <sc:sd/sd> assert_equals: failure should set href to input expected "i" but got ""
-PASS Parsing: <i> against <sc:/pa/pa>
-FAIL Parsing: <i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <i> against <sc:///pa/pa> assert_equals: pathname expected "/pa/i" but got "///pa/i"
-FAIL Parsing: <../i> against <sc:sd> assert_equals: failure should set href to input expected "../i" but got ""
-FAIL Parsing: <../i> against <sc:sd/sd> assert_equals: failure should set href to input expected "../i" but got ""
-PASS Parsing: <../i> against <sc:/pa/pa>
-FAIL Parsing: <../i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <../i> against <sc:///pa/pa> assert_equals: href expected "sc:///i" but got "sc:///pa/i"
-FAIL Parsing: </i> against <sc:sd> assert_equals: failure should set href to input expected "/i" but got ""
-FAIL Parsing: </i> against <sc:sd/sd> assert_equals: failure should set href to input expected "/i" but got ""
-PASS Parsing: </i> against <sc:/pa/pa>
-FAIL Parsing: </i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: </i> against <sc:///pa/pa> assert_equals: href expected "sc:///i" but got "sc:///pa/i"
-FAIL Parsing: <?i> against <sc:sd> assert_equals: failure should set href to input expected "?i" but got ""
-FAIL Parsing: <?i> against <sc:sd/sd> assert_equals: failure should set href to input expected "?i" but got ""
-PASS Parsing: <?i> against <sc:/pa/pa>
-FAIL Parsing: <?i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <?i> against <sc:///pa/pa> assert_equals: pathname expected "/pa/pa" but got "///pa/pa"
-PASS Parsing: <#i> against <sc:sd>
-PASS Parsing: <#i> against <sc:sd/sd>
-PASS Parsing: <#i> against <sc:/pa/pa>
-FAIL Parsing: <#i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <#i> against <sc:///pa/pa> assert_equals: pathname expected "/pa/pa" but got "///pa/pa"
-FAIL Parsing: <about:/../> against <about:blank> assert_equals: href expected "about:/" but got "about:/../"
-FAIL Parsing: <data:/../> against <about:blank> assert_equals: href expected "data:/" but got "data:/../"
-FAIL Parsing: <javascript:/../> against <about:blank> assert_equals: href expected "javascript:/" but got "javascript:/../"
-FAIL Parsing: <mailto:/../> against <about:blank> assert_equals: href expected "mailto:/" but got "mailto:/../"
-FAIL Parsing: <sc://ñ.test/> against <about:blank> assert_equals: host expected "%C3%B1.test" but got ""
-FAIL Parsing: <sc://\0/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc:// /> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://%/> against <about:blank> assert_equals: host expected "%" but got ""
-FAIL Parsing: <sc://@/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://te@s:t@/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://:/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://:12/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://[/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://\/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://]/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <x> against <sc://ñ> assert_equals: href expected "sc://%C3%B1/x" but got "sc://%C3%B1"
-PASS Parsing: <sc:\../> against <about:blank>
-PASS Parsing: <sc::a@example.net> against <about:blank>
-PASS Parsing: <wow:%NBD> against <about:blank>
-PASS Parsing: <wow:%1G> against <about:blank>
-FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
-FAIL Parsing: <http://example.com/U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿?U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿> against <about:blank> assert_equals: href expected "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF" but got "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%BF%BD%EF%B7%8F%EF%BF%BD%EF%B7%B0%EF%BF%BD%EF%BF%BD?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%BF%BD%EF%B7%8F%EF%BF%BD%EF%B7%B0%EF%BF%BD%EF%BF%BD"
-FAIL Parsing: <http://a<b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://a>b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://a^b> against <about:blank> assert_equals: failure should set href to input expected "http://a^b" but got "http://a%5Eb/"
-FAIL Parsing: <non-special://a<b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <non-special://a>b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <non-special://a^b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <foo://ho\0st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <foo://ho|st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <foo://ho	st/> against <about:blank> assert_equals: host expected "host" but got ""
-FAIL Parsing: <foo://ho
-st/> against <about:blank> assert_equals: host expected "host" but got ""
-FAIL Parsing: <foo://ho\rst/> against <about:blank> assert_equals: host expected "host" but got ""
-PASS Parsing: <http://ho%00st/> against <about:blank>
-PASS Parsing: <http://ho%09st/> against <about:blank>
-PASS Parsing: <http://ho%0Ast/> against <about:blank>
-PASS Parsing: <http://ho%0Dst/> against <about:blank>
-FAIL Parsing: <http://ho%20st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://ho%23st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://ho%2Fst/> against <about:blank>
-FAIL Parsing: <http://ho%3Ast/> against <about:blank> assert_equals: failure should set href to input expected "http://ho%3Ast/" but got "http://ho:st/"
-FAIL Parsing: <http://ho%3Cst/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://ho%3Est/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://ho%3Fst/> against <about:blank>
-FAIL Parsing: <http://ho%40st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://ho%5Bst/> against <about:blank> assert_equals: failure should set href to input expected "http://ho%5Bst/" but got "http://ho[st/"
-PASS Parsing: <http://ho%5Cst/> against <about:blank>
-FAIL Parsing: <http://ho%5Dst/> against <about:blank> assert_equals: failure should set href to input expected "http://ho%5Dst/" but got "http://ho]st/"
-FAIL Parsing: <http://ho%7Cst/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: href expected "http://\x1f!\"$&'()*+,-.;=_`{}~/" but got "http://%1F%21%22%24%26%27%28%29%2A+%2C-.%3B%3D_%60%7B%7D%7E/"
-FAIL Parsing: <sc://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: host expected "%1F!\"$&'()*+,-.;=_`{}~" but got ""
-FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
-FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
-FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
-FAIL Parsing: <https://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%A0/" but got "https://example.com%EF%BF%BD/"
-PASS Parsing: <ftp://%e2%98%83> against <about:blank>
-PASS Parsing: <https://%e2%98%83> against <about:blank>
-PASS Parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
-PASS Parsing: <http://facebook.com/?foo=%7B%22abc%22> against <about:blank>
-PASS Parsing: <https://localhost:3000/jqueryui@1.2.3> against <about:blank>
-PASS Parsing: <h	t
-t\rp://h	o
-s\rt:9	0
-0\r0/p	a
-t\rh?q	u
-e\rry#f	r
-a\rg> against <about:blank>
-PASS Parsing: <?a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing: <??a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing: <http:> against <http://example.org/foo/bar>
-PASS Parsing: <http:> against <https://example.org/foo/bar>
-PASS Parsing: <sc:> against <https://example.org/foo/bar>
-PASS Parsing: <http://foo.bar/baz?qux#foobar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo"bar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
-PASS Parsing: <http://192.168.257> against <http://other.com/>
-PASS Parsing: <http://192.168.257.com> against <http://other.com/>
-PASS Parsing: <http://256> against <http://other.com/>
-PASS Parsing: <http://256.com> against <http://other.com/>
-PASS Parsing: <http://999999999> against <http://other.com/>
-PASS Parsing: <http://999999999.com> against <http://other.com/>
-FAIL Parsing: <http://10000000000> against <http://other.com/> assert_equals: failure should set href to input expected "http://10000000000" but got "http://10000000000/"
-PASS Parsing: <http://10000000000.com> against <http://other.com/>
-PASS Parsing: <http://4294967295> against <http://other.com/>
-FAIL Parsing: <http://4294967296> against <http://other.com/> assert_equals: failure should set href to input expected "http://4294967296" but got "http://4294967296/"
-PASS Parsing: <http://0xffffffff> against <http://other.com/>
-FAIL Parsing: <http://0xffffffff1> against <http://other.com/> assert_equals: failure should set href to input expected "http://0xffffffff1" but got "http://0xffffffff1/"
-FAIL Parsing: <http://256.256.256.256> against <http://other.com/> assert_equals: failure should set href to input expected "http://256.256.256.256" but got "http://256.256.256.256/"
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
-PASS Parsing: <https://0x.0x.0> against <about:blank>
-PASS Parsing: <https://0x100000000/test> against <about:blank>
-PASS Parsing: <https://256.0.0.1/test> against <about:blank>
-PASS Parsing: <file:///C%3A/> against <about:blank>
-PASS Parsing: <file:///C%7C/> against <about:blank>
-FAIL Parsing: <file://%43%3A> against <about:blank> assert_equals: failure should set href to input expected "file://%43%3A" but got "file://c:/"
-FAIL Parsing: <file://%43%7C> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <file://%43|> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <file://C%7C> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <file://%43%7C/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <https://%43%7C/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <asdf://%43|/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <asdf://%43%7C/> against <about:blank> assert_equals: host expected "%43%7C" but got ""
-PASS Parsing: <pix/submit.gif> against <file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html>
-FAIL Parsing: <..> against <file:///C:/> assert_equals: href expected "file:///C:/" but got "file:///"
-PASS Parsing: <..> against <file:///>
-FAIL Parsing: </> against <file:///C:/a/b> assert_equals: href expected "file:///C:/" but got "file:///"
-FAIL Parsing: </> against <file://h/C:/a/b> assert_equals: href expected "file://h/C:/" but got "file:///"
-FAIL Parsing: </> against <file://h/a/b> assert_equals: href expected "file://h/" but got "file:///"
-FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file://d:/"
-FAIL Parsing: <//d:/..> against <file:///C:/a/b> assert_equals: href expected "file:///d:/" but got "file://d:/"
-PASS Parsing: <..> against <file:///ab:/>
-PASS Parsing: <..> against <file:///1:/>
-PASS Parsing: <> against <file:///test?test#test>
-PASS Parsing: <file:> against <file:///test?test#test>
-PASS Parsing: <?x> against <file:///test?test#test>
-PASS Parsing: <file:?x> against <file:///test?test#test>
-PASS Parsing: <#x> against <file:///test?test#test>
-PASS Parsing: <file:#x> against <file:///test?test#test>
-FAIL Parsing: <file:\\//> against <about:blank> assert_equals: href expected "file:////" but got "file:///"
-FAIL Parsing: <file:\\\\> against <about:blank> assert_equals: href expected "file:////" but got "file:///"
-FAIL Parsing: <file:\\\\?fox> against <about:blank> assert_equals: href expected "file:////?fox" but got "file:///?fox"
-FAIL Parsing: <file:\\\\#guppy> against <about:blank> assert_equals: href expected "file:////#guppy" but got "file:///#guppy"
-PASS Parsing: <file://spider///> against <about:blank>
-FAIL Parsing: <file:\\localhost//> against <about:blank> assert_equals: href expected "file:////" but got "file://localhost//"
-PASS Parsing: <file:///localhost//cat> against <about:blank>
-FAIL Parsing: <file://\/localhost//cat> against <about:blank> assert_equals: href expected "file:////localhost//cat" but got "file:///localhost//cat"
-FAIL Parsing: <file://localhost//a//../..//> against <about:blank> assert_equals: href expected "file://///" but got "file://localhost///"
-FAIL Parsing: </////mouse> against <file:///elephant> assert_equals: href expected "file://///mouse" but got "file:///mouse"
-PASS Parsing: <\//pig> against <file://lion/>
-FAIL Parsing: <\/localhost//pig> against <file://lion/> assert_equals: href expected "file:////pig" but got "file://localhost//pig"
-FAIL Parsing: <//localhost//pig> against <file://lion/> assert_equals: href expected "file:////pig" but got "file://localhost//pig"
-PASS Parsing: </..//localhost//pig> against <file://lion/>
-PASS Parsing: <file://> against <file://ape/>
-PASS Parsing: </rooibos> against <file://tea/>
-PASS Parsing: </?chai> against <file://tea/>
-FAIL Parsing: <C|> against <file://host/dir/file> assert_equals: href expected "file://host/C:" but got "file://host/dir/C%7C"
-FAIL Parsing: <C|> against <file://host/D:/dir1/dir2/file> assert_equals: href expected "file://host/C:" but got "file://host/D:/dir1/dir2/C%7C"
-FAIL Parsing: <C|#> against <file://host/dir/file> assert_equals: href expected "file://host/C:#" but got "file://host/dir/C%7C#"
-FAIL Parsing: <C|?> against <file://host/dir/file> assert_equals: href expected "file://host/C:?" but got "file://host/dir/C%7C?"
-FAIL Parsing: <C|/> against <file://host/dir/file> assert_equals: href expected "file://host/C:/" but got "file://host/dir/C%7C/"
-FAIL Parsing: <C|
-/> against <file://host/dir/file> assert_equals: href expected "file://host/C:/" but got "file://host/dir/C%7C/"
-FAIL Parsing: <C|\> against <file://host/dir/file> assert_equals: href expected "file://host/C:/" but got "file://host/dir/C%7C/"
-PASS Parsing: <C> against <file://host/dir/file>
-FAIL Parsing: <C|a> against <file://host/dir/file> assert_equals: href expected "file://host/dir/C|a" but got "file://host/dir/C%7Ca"
-PASS Parsing: </c:/foo/bar> against <file:///c:/baz/qux>
-FAIL Parsing: </c|/foo/bar> against <file:///c:/baz/qux> assert_equals: href expected "file:///c:/foo/bar" but got "file:///c%7C/foo/bar"
-PASS Parsing: <file:\c:\foo\bar> against <file:///c:/baz/qux>
-PASS Parsing: </c:/foo/bar> against <file://host/path>
-PASS Parsing: <file://example.net/C:/> against <about:blank>
-PASS Parsing: <file://1.2.3.4/C:/> against <about:blank>
-PASS Parsing: <file://[1::8]/C:/> against <about:blank>
-FAIL Parsing: <C|/> against <file://host/> assert_equals: href expected "file://host/C:/" but got "file://host/C%7C/"
-PASS Parsing: </C:/> against <file://host/>
-PASS Parsing: <file:C:/> against <file://host/>
-PASS Parsing: <file:/C:/> against <file://host/>
-FAIL Parsing: <//C:/> against <file://host/> assert_equals: href expected "file:///C:/" but got "file://c:/"
-FAIL Parsing: <file://C:/> against <file://host/> assert_equals: href expected "file:///C:/" but got "file://c:/"
-PASS Parsing: <///C:/> against <file://host/>
-PASS Parsing: <file:///C:/> against <file://host/>
-FAIL Parsing: <file:/C|/> against <about:blank> assert_equals: href expected "file:///C:/" but got "file:///C%7C/"
-FAIL Parsing: <file://C|/> against <about:blank> assert_equals: href expected "file:///C:/" but got "file://c%7C/"
-PASS Parsing: <file:> against <about:blank>
-PASS Parsing: <file:?q=v> against <about:blank>
-PASS Parsing: <file:#frag> against <about:blank>
-PASS Parsing: <file:///Y:> against <about:blank>
-PASS Parsing: <file:///Y:/> against <about:blank>
-PASS Parsing: <file:///./Y> against <about:blank>
-PASS Parsing: <file:///./Y:> against <about:blank>
-FAIL Parsing: <\\\.\Y:> against <about:blank> assert_equals: failure should set href to input expected "\\\\\\.\\Y:" but got ""
-PASS Parsing: <file:///y:> against <about:blank>
-PASS Parsing: <file:///y:/> against <about:blank>
-PASS Parsing: <file:///./y> against <about:blank>
-PASS Parsing: <file:///./y:> against <about:blank>
-FAIL Parsing: <\\\.\y:> against <about:blank> assert_equals: failure should set href to input expected "\\\\\\.\\y:" but got ""
-FAIL Parsing: <file://localhost//a//../..//foo> against <about:blank> assert_equals: href expected "file://///foo" but got "file://localhost///foo"
-FAIL Parsing: <file://localhost////foo> against <about:blank> assert_equals: href expected "file://////foo" but got "file://localhost////foo"
-FAIL Parsing: <file:////foo> against <about:blank> assert_equals: href expected "file:////foo" but got "file:///foo"
-PASS Parsing: <file:///one/two> against <file:///>
-FAIL Parsing: <file:////one/two> against <file:///> assert_equals: href expected "file:////one/two" but got "file:///one/two"
-PASS Parsing: <//one/two> against <file:///>
-PASS Parsing: <///one/two> against <file:///>
-FAIL Parsing: <////one/two> against <file:///> assert_equals: href expected "file:////one/two" but got "file:///one/two"
-PASS Parsing: <file:///.//> against <file:////>
-PASS Parsing: <file:.//p> against <about:blank>
-PASS Parsing: <file:/.//p> against <about:blank>
-PASS Parsing: <http://[1:0::]> against <http://example.net/>
-FAIL Parsing: <http://[0:1:2:3:4:5:6:7:8]> against <http://example.net/> assert_equals: failure should set href to input expected "http://[0:1:2:3:4:5:6:7:8]" but got "http://[0:1:2:3:4:5:6:7:8]/"
-FAIL Parsing: <https://[0::0::0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0::0::0]" but got "https://[0::0::0]/"
-FAIL Parsing: <https://[0:.0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:.0]" but got "https://[0:.0]/"
-FAIL Parsing: <https://[0:0:]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:0:]" but got "https://[0:0:]/"
-FAIL Parsing: <https://[0:1:2:3:4:5:6:7.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1:2:3:4:5:6:7.0.0.0.1]" but got "https://[0:1:2:3:4:5:6:7.0.0.0.1]/"
-FAIL Parsing: <https://[0:1.00.0.0.0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1.00.0.0.0]" but got "https://[0:1.00.0.0.0]/"
-FAIL Parsing: <https://[0:1.290.0.0.0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1.290.0.0.0]" but got "https://[0:1.290.0.0.0]/"
-FAIL Parsing: <https://[0:1.23.23]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1.23.23]" but got "https://[0:1.23.23]/"
-FAIL Parsing: <http://?> against <about:blank> assert_equals: failure should set href to input expected "http://?" but got "http:/?"
-FAIL Parsing: <http://#> against <about:blank> assert_equals: failure should set href to input expected "http://#" but got "http:/#"
-PASS Parsing: <http://f:4294967377/c> against <http://example.org/>
-PASS Parsing: <http://f:18446744073709551697/c> against <http://example.org/>
-PASS Parsing: <http://f:340282366920938463463374607431768211537/c> against <http://example.org/>
-FAIL Parsing: <sc://ñ> against <about:blank> assert_equals: host expected "%C3%B1" but got ""
-FAIL Parsing: <sc://ñ?x> against <about:blank> assert_equals: host expected "%C3%B1" but got ""
-FAIL Parsing: <sc://ñ#x> against <about:blank> assert_equals: host expected "%C3%B1" but got ""
-FAIL Parsing: <#x> against <sc://ñ> assert_equals: href expected "sc://%C3%B1#x" but got "sc://%C3%B1"
-FAIL Parsing: <?x> against <sc://ñ> assert_equals: href expected "sc://%C3%B1?x" but got "sc://%C3%B1"
-FAIL Parsing: <sc://?> against <about:blank> assert_equals: pathname expected "" but got "//"
-FAIL Parsing: <sc://#> against <about:blank> assert_equals: pathname expected "" but got "//"
-FAIL Parsing: <///> against <sc://x/> assert_equals: href expected "sc:///" but got "sc:"
-FAIL Parsing: <////> against <sc://x/> assert_equals: href expected "sc:////" but got "sc:"
-FAIL Parsing: <////x/> against <sc://x/> assert_equals: href expected "sc:////x/" but got "sc://x/"
-FAIL Parsing: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank> assert_equals: host expected "foobar.com" but got ""
-FAIL Parsing: <telnet://user:pass@foobar.com:23/> against <about:blank> assert_equals: username expected "user" but got ""
-FAIL Parsing: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank> assert_equals: host expected "10.10.10.10:7777" but got ""
-FAIL Parsing: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank> assert_equals: username expected "foo" but got ""
-FAIL Parsing: <rsync://foo@host:911/sup> against <about:blank> assert_equals: username expected "foo" but got ""
-FAIL Parsing: <git://github.com/foo/bar.git> against <about:blank> assert_equals: host expected "github.com" but got ""
-FAIL Parsing: <irc://myserver.com:6999/channel?passwd> against <about:blank> assert_equals: host expected "myserver.com:6999" but got ""
-FAIL Parsing: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank> assert_equals: host expected "fw.example.org:9999" but got ""
-FAIL Parsing: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank> assert_equals: host expected "localhost:389" but got ""
-FAIL Parsing: <git+https://github.com/foo/bar> against <about:blank> assert_equals: host expected "github.com" but got ""
-PASS Parsing: <urn:ietf:rfc:2648> against <about:blank>
-PASS Parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
-FAIL Parsing: <non-spec:/.//> against <about:blank> assert_equals: pathname expected "//" but got "/.//"
-FAIL Parsing: <non-spec:/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec:/..//"
-FAIL Parsing: <non-spec:/a/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec:/a/..//"
-FAIL Parsing: <non-spec:/.//path> against <about:blank> assert_equals: pathname expected "//path" but got "/.//path"
-FAIL Parsing: <non-spec:/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec:/..//path"
-FAIL Parsing: <non-spec:/a/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec:/a/..//path"
-FAIL Parsing: </.//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: </..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: <..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: <a/..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: <> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//p" but got "non-spec:/..//p"
-FAIL Parsing: <path> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//path" but got "non-spec:/..//path"
-FAIL Parsing: <../path> against <non-spec:/.//p> assert_equals: href expected "non-spec:/path" but got "non-spec:/./path"
-FAIL Parsing: <non-special://%E2%80%A0/> against <about:blank> assert_equals: host expected "%E2%80%A0" but got ""
-FAIL Parsing: <non-special://H%4fSt/path> against <about:blank> assert_equals: host expected "H%4fSt" but got ""
-FAIL Parsing: <non-special://[1:2:0:0:5:0:0:0]/> against <about:blank> assert_equals: href expected "non-special://[1:2:0:0:5::]/" but got "non-special://[1:2:0:0:5:0:0:0]/"
-FAIL Parsing: <non-special://[1:2:0:0:0:0:0:3]/> against <about:blank> assert_equals: href expected "non-special://[1:2::3]/" but got "non-special://[1:2:0:0:0:0:0:3]/"
-FAIL Parsing: <non-special://[1:2::3]:80/> against <about:blank> assert_equals: host expected "[1:2::3]:80" but got ""
-FAIL Parsing: <non-special://[:80/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <blob:https://example.com:443/> against <about:blank>
-PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
-PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
-PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
-FAIL Parsing: <http://[::127.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "http://[::127.0.0.0.1]" but got "http://[::127.0.0.0.1]/"
-PASS Parsing: <http://[0:1:0:1:0:1:0:1]> against <about:blank>
-PASS Parsing: <http://[1:0:1:0:1:0:1:0]> against <about:blank>
-PASS Parsing: <http://example.org/test?"> against <about:blank>
-PASS Parsing: <http://example.org/test?#> against <about:blank>
-PASS Parsing: <http://example.org/test?<> against <about:blank>
-PASS Parsing: <http://example.org/test?>> against <about:blank>
-PASS Parsing: <http://example.org/test?⌣> against <about:blank>
-PASS Parsing: <http://example.org/test?%23%23> against <about:blank>
-PASS Parsing: <http://example.org/test?%GH> against <about:blank>
-PASS Parsing: <http://example.org/test?a#%EF> against <about:blank>
-PASS Parsing: <http://example.org/test?a#%GH> against <about:blank>
-FAIL Parsing: <a> against <about:blank> assert_equals: failure should set href to input expected "a" but got ""
-FAIL Parsing: <a/> against <about:blank> assert_equals: failure should set href to input expected "a/" but got ""
-FAIL Parsing: <a//> against <about:blank> assert_equals: failure should set href to input expected "a//" but got ""
-FAIL Parsing: <test-a-colon.html> against <a:> assert_equals: failure should set href to input expected "test-a-colon.html" but got ""
-FAIL Parsing: <test-a-colon-b.html> against <a:b> assert_equals: failure should set href to input expected "test-a-colon-b.html" but got ""
-PASS Parsing: <test-a-colon-slash.html> against <a:/>
-FAIL Parsing: <test-a-colon-slash-slash.html> against <a://> assert_equals: href expected "a:///test-a-colon-slash-slash.html" but got ""
-PASS Parsing: <test-a-colon-slash-b.html> against <a:/b>
-FAIL Parsing: <test-a-colon-slash-slash-b.html> against <a://b> assert_equals: href expected "a://b/test-a-colon-slash-slash-b.html" but got "a://b"
-PASS Parsing: <http://example.org/test?a#b\0c> against <about:blank>
-FAIL Parsing: <non-spec://example.org/test?a#b\0c> against <about:blank> assert_equals: host expected "example.org" but got ""
-PASS Parsing: <non-spec:/test?a#b\0c> against <about:blank>
-PASS Parsing: <10.0.0.7:8080/foo.html> against <file:///some/dir/bar.html>
-PASS Parsing: <a!@$*=/foo.html> against <file:///some/dir/bar.html>
-PASS Parsing: <a1234567890-+.:foo/bar> against <http://example.com/dir/file>
-PASS Parsing: <file://a­b/p> against <about:blank>
-PASS Parsing: <file://a%C2%ADb/p> against <about:blank>
-FAIL Parsing: <file://­/p> against <about:blank> assert_equals: failure should set href to input expected "file://­/p" but got "file://%C2%AD/p"
-PASS Parsing: <file://%C2%AD/p> against <about:blank>
-FAIL Parsing: <file://xn--/p> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <#link> against <https://example.org/##link>
-PASS Parsing: <non-special:cannot-be-a-base-url-\0~€> against <about:blank>
-PASS Parsing: <https://www.example.com/path{path.html?query'=query#fragment<fragment> against <about:blank>
-PASS Parsing: <https://user:pass[@foo/bar> against <http://example.org>
-FAIL Parsing: <foo:// !"$%&'()*+,-.;<=>@[\]^_`{|}~@host/> against <about:blank> assert_equals: href expected "foo://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/" but got "foo:// !\"$%&'()*+,-.;<=>@[\\]^_`{|}~@host/"
-FAIL Parsing: <wss:// !"$%&'()*+,-.;<=>@[]^_`{|}~@host/> against <about:blank> assert_equals: href expected "wss://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/" but got "wss://%20!%22$%&%27()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/"
-FAIL Parsing: <foo://joe: !"$%&'()*+,-.:;<=>@[\]^_`{|}~@host/> against <about:blank> assert_equals: href expected "foo://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/" but got "foo://joe: !\"$%&'()*+,-.:;<=>@[\\]^_`{|}~@host/"
-FAIL Parsing: <wss://joe: !"$%&'()*+,-.:;<=>@[]^_`{|}~@host/> against <about:blank> assert_equals: href expected "wss://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/" but got "wss://joe:%20!%22$%&%27()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/"
-FAIL Parsing: <foo://!"$%&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: host expected "!\"$%&'()*+,-.;=_`{}~" but got ""
-FAIL Parsing: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: href expected "wss://!\"$&'()*+,-.;=_`{}~/" but got "wss://%21%22%24%26%27%28%29%2A+%2C-.%3B%3D_%60%7B%7D%7E/"
-FAIL Parsing: <foo://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank> assert_equals: href expected "foo://host/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~" but got "foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~"
-FAIL Parsing: <wss://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank> assert_equals: href expected "wss://host/%20!%22$%&'()*+,-./:;%3C=%3E@[/]^_%60%7B|%7D~" but got "wss://host/%20!%22$%&'()*+,-./:;%3C=%3E@[/]%5E_%60%7B%7C%7D~"
-FAIL Parsing: <foo://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank> assert_equals: href expected "foo://host/dir/?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~" but got "foo://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
-PASS Parsing: <wss://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-FAIL Parsing: <foo://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank> assert_equals: host expected "host" but got ""
-PASS Parsing: <wss://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-FAIL Parsing: <abc:rootless> against <abc://host/path> assert_equals: href expected "abc:rootless" but got "abc://host/rootless"
-FAIL Parsing: <abc:rootless> against <abc:/path> assert_equals: href expected "abc:rootless" but got "abc:/rootless"
-PASS Parsing: <abc:rootless> against <abc:path>
-FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-origin-expected.txt
deleted file mode 100644
index e1738cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-origin-expected.txt
+++ /dev/null
@@ -1,337 +0,0 @@
-This is a testharness.js-based test.
-Found 324 tests; 319 PASS, 5 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Loading data…
-PASS Parsing origin: <http://example	.
-org> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://user:pass@foo:21/bar;par?b#c> against <http://example.org/foo/bar>
-PASS Parsing origin: <https://test:@test> against <about:blank>
-PASS Parsing origin: <https://:@test> against <about:blank>
-PASS Parsing origin: <non-special://test:@test/x> against <about:blank>
-PASS Parsing origin: <non-special://:@test/x> against <about:blank>
-PASS Parsing origin: <http:foo.com> against <http://example.org/foo/bar>
-PASS Parsing origin: <	   :foo.com   
-> against <http://example.org/foo/bar>
-PASS Parsing origin: < foo.com  > against <http://example.org/foo/bar>
-PASS Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:
-/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <> against <http://example.org/foo/bar>
-PASS Parsing origin: <  	> against <http://example.org/foo/bar>
-PASS Parsing origin: <:foo.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <:foo.com\> against <http://example.org/foo/bar>
-PASS Parsing origin: <:> against <http://example.org/foo/bar>
-PASS Parsing origin: <:a> against <http://example.org/foo/bar>
-PASS Parsing origin: <:/> against <http://example.org/foo/bar>
-PASS Parsing origin: <:\> against <http://example.org/foo/bar>
-PASS Parsing origin: <:#> against <http://example.org/foo/bar>
-PASS Parsing origin: <#> against <http://example.org/foo/bar>
-PASS Parsing origin: <#/> against <http://example.org/foo/bar>
-PASS Parsing origin: <#\> against <http://example.org/foo/bar>
-PASS Parsing origin: <#;?> against <http://example.org/foo/bar>
-PASS Parsing origin: <?> against <http://example.org/foo/bar>
-PASS Parsing origin: </> against <http://example.org/foo/bar>
-PASS Parsing origin: <:23> against <http://example.org/foo/bar>
-PASS Parsing origin: </:23> against <http://example.org/foo/bar>
-PASS Parsing origin: <\x> against <http://example.org/foo/bar>
-PASS Parsing origin: <\\x\hello> against <http://example.org/foo/bar>
-PASS Parsing origin: <::> against <http://example.org/foo/bar>
-PASS Parsing origin: <::23> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo://> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://a:b@c:29/d> against <http://example.org/foo/bar>
-PASS Parsing origin: <http::@c:29> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://&a:foo(b]c@d:2/> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://::@c@d:2> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo.com:b@d/> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo.com/\@> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:\\foo.com\> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:\\a\b:c\d@foo.com\> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo:/> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo:/bar.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo://///////> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo://///////bar.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo:////://///> against <http://example.org/foo/bar>
-PASS Parsing origin: <c:/foo> against <http://example.org/foo/bar>
-PASS Parsing origin: <//foo/bar> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo/path;a??e#f#g> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo/abcd?efgh?ijkl> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo/abcd#foo?bar> against <http://example.org/foo/bar>
-PASS Parsing origin: <[61:24:74]:98> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:[61:27]/:foo> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[2001::1]> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[::127.0.0.1]> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[2001::1]:80> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftp:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <https:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <madeupscheme:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftps:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <gopher:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ws:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <wss:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <data:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <javascript:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <mailto:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftp:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <https:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <madeupscheme:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftps:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <gopher:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ws:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <wss:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <data:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <javascript:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <mailto:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: </a/b/c> against <http://example.org/foo/bar>
-PASS Parsing origin: </a/ /c> against <http://example.org/foo/bar>
-PASS Parsing origin: </a%2fc> against <http://example.org/foo/bar>
-PASS Parsing origin: </a/%2f/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <#β> against <http://example.org/foo/bar>
-PASS Parsing origin: <data:text/html,test#test> against <http://example.org/foo/bar>
-PASS Parsing origin: <tel:1234567890> against <http://example.org/foo/bar>
-PASS Parsing origin: <ssh://example.com/foo/bar.git> against <http://example.org/>
-PASS Parsing origin: <http://example.com/././foo> against <about:blank>
-PASS Parsing origin: <http://example.com/./.foo> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/.> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/./> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/../> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/..bar> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/../ton> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/../ton/../../a> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/../../..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/../../../ton> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/%2e> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/%2e%2> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank>
-PASS Parsing origin: <http://example.com////../..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar//../..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar//..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo> against <about:blank>
-PASS Parsing origin: <http://example.com/%20foo> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%2> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%2zbar> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%2©zbar> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%41%7a> against <about:blank>
-PASS Parsing origin: <http://example.com/foo	‘%91> against <about:blank>
-FAIL Parsing origin: <http://example.com/foo%00%51> against <about:blank> assert_equals: origin expected "http://example.com" but got "null"
-PASS Parsing origin: <http://example.com/(%28:%3A%29)> against <about:blank>
-PASS Parsing origin: <http://example.com/%3A%3a%3C%3c> against <about:blank>
-PASS Parsing origin: <http://example.com/foo	bar> against <about:blank>
-PASS Parsing origin: <http://example.com\\foo\\bar> against <about:blank>
-PASS Parsing origin: <http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd> against <about:blank>
-PASS Parsing origin: <http://example.com/@asdf%40> against <about:blank>
-PASS Parsing origin: <http://example.com/你好你好> against <about:blank>
-PASS Parsing origin: <http://example.com/‥/foo> against <about:blank>
-PASS Parsing origin: <http://example.com//foo> against <about:blank>
-PASS Parsing origin: <http://example.com/‮/foo/‭/bar> against <about:blank>
-PASS Parsing origin: <http://www.google.com/foo?bar=baz#> against <about:blank>
-PASS Parsing origin: <http://www.google.com/foo?bar=baz# »> against <about:blank>
-PASS Parsing origin: <data:test# »> against <about:blank>
-PASS Parsing origin: <http://www.google.com> against <about:blank>
-PASS Parsing origin: <http://192.0x00A80001> against <about:blank>
-PASS Parsing origin: <http://www/foo%2Ehtml> against <about:blank>
-PASS Parsing origin: <http://www/foo/%2E/html> against <about:blank>
-PASS Parsing origin: <http://%25DOMAIN:foobar@foodomain.com/> against <about:blank>
-PASS Parsing origin: <http:\\www.google.com\foo> against <about:blank>
-PASS Parsing origin: <http://foo:80/> against <about:blank>
-PASS Parsing origin: <http://foo:81/> against <about:blank>
-PASS Parsing origin: <httpa://foo:80/> against <about:blank>
-PASS Parsing origin: <https://foo:443/> against <about:blank>
-PASS Parsing origin: <https://foo:80/> against <about:blank>
-PASS Parsing origin: <ftp://foo:21/> against <about:blank>
-PASS Parsing origin: <ftp://foo:80/> against <about:blank>
-PASS Parsing origin: <gopher://foo:70/> against <about:blank>
-PASS Parsing origin: <gopher://foo:443/> against <about:blank>
-PASS Parsing origin: <ws://foo:80/> against <about:blank>
-PASS Parsing origin: <ws://foo:81/> against <about:blank>
-PASS Parsing origin: <ws://foo:443/> against <about:blank>
-PASS Parsing origin: <ws://foo:815/> against <about:blank>
-PASS Parsing origin: <wss://foo:80/> against <about:blank>
-PASS Parsing origin: <wss://foo:81/> against <about:blank>
-PASS Parsing origin: <wss://foo:443/> against <about:blank>
-PASS Parsing origin: <wss://foo:815/> against <about:blank>
-PASS Parsing origin: <http:/example.com/> against <about:blank>
-PASS Parsing origin: <ftp:/example.com/> against <about:blank>
-PASS Parsing origin: <https:/example.com/> against <about:blank>
-PASS Parsing origin: <madeupscheme:/example.com/> against <about:blank>
-PASS Parsing origin: <ftps:/example.com/> against <about:blank>
-PASS Parsing origin: <gopher:/example.com/> against <about:blank>
-PASS Parsing origin: <ws:/example.com/> against <about:blank>
-PASS Parsing origin: <wss:/example.com/> against <about:blank>
-PASS Parsing origin: <data:/example.com/> against <about:blank>
-PASS Parsing origin: <javascript:/example.com/> against <about:blank>
-PASS Parsing origin: <mailto:/example.com/> against <about:blank>
-PASS Parsing origin: <http:example.com/> against <about:blank>
-PASS Parsing origin: <ftp:example.com/> against <about:blank>
-PASS Parsing origin: <https:example.com/> against <about:blank>
-PASS Parsing origin: <madeupscheme:example.com/> against <about:blank>
-PASS Parsing origin: <ftps:example.com/> against <about:blank>
-PASS Parsing origin: <gopher:example.com/> against <about:blank>
-PASS Parsing origin: <ws:example.com/> against <about:blank>
-PASS Parsing origin: <wss:example.com/> against <about:blank>
-PASS Parsing origin: <data:example.com/> against <about:blank>
-PASS Parsing origin: <javascript:example.com/> against <about:blank>
-PASS Parsing origin: <mailto:example.com/> against <about:blank>
-PASS Parsing origin: <http:@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/@www.example.com> against <about:blank>
-PASS Parsing origin: <http://@www.example.com> against <about:blank>
-PASS Parsing origin: <http:a:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/a:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http://a:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http://@pple.com> against <about:blank>
-PASS Parsing origin: <http::b@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http://:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http:a:@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/a:@www.example.com> against <about:blank>
-PASS Parsing origin: <http://a:@www.example.com> against <about:blank>
-PASS Parsing origin: <http://www.@pple.com> against <about:blank>
-PASS Parsing origin: <http://:@www.example.com> against <about:blank>
-PASS Parsing origin: </> against <http://www.example.com/test>
-PASS Parsing origin: </test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <.> against <http://www.example.com/test>
-PASS Parsing origin: <..> against <http://www.example.com/test>
-PASS Parsing origin: <test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <./test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <../test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <../aaa/test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <../../test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <中/test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <http://www.example2.com> against <http://www.example.com/test>
-PASS Parsing origin: <//www.example2.com> against <http://www.example.com/test>
-PASS Parsing origin: <http://ExAmPlE.CoM> against <http://other.com/>
-PASS Parsing origin: <http://GOO​⁠goo.com> against <http://other.com/>
-PASS Parsing origin: <\0 http://example.com/ \r > against <about:blank>
-PASS Parsing origin: <http://www.foo。bar.com> against <http://other.com/>
-PASS Parsing origin: <https://x/�?�#�> against <about:blank>
-PASS Parsing origin: <http://Go.com> against <http://other.com/>
-PASS Parsing origin: <http://你好你好> against <http://other.com/>
-FAIL Parsing origin: <https://faß.ExAmPlE/> against <about:blank> assert_equals: origin expected "https://xn--fa-hia.example" but got "https://fass.example"
-PASS Parsing origin: <sc://faß.ExAmPlE/> against <about:blank>
-PASS Parsing origin: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>
-PASS Parsing origin: <http://%30%78%63%30%2e%30%32%35%30.01%2e> against <http://other.com/>
-PASS Parsing origin: <http://0Xc0.0250.01> against <http://other.com/>
-PASS Parsing origin: <http://./> against <about:blank>
-PASS Parsing origin: <http://../> against <about:blank>
-PASS Parsing origin: <http://0..0x300/> against <about:blank>
-PASS Parsing origin: <http://foo:💩@example.com/bar> against <http://other.com/>
-PASS Parsing origin: <#> against <test:test>
-PASS Parsing origin: <#x> against <mailto:x@x.com>
-PASS Parsing origin: <#x> against <data:,>
-PASS Parsing origin: <#x> against <about:blank>
-PASS Parsing origin: <#> against <test:test?test>
-PASS Parsing origin: <https://@test@test@example:800/> against <http://doesnotmatter/>
-PASS Parsing origin: <https://@@@example> against <http://doesnotmatter/>
-PASS Parsing origin: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>
-PASS Parsing origin: <http://host/?'> against <about:blank>
-PASS Parsing origin: <notspecial://host/?'> against <about:blank>
-PASS Parsing origin: </some/path> against <http://user@example.org/smth>
-PASS Parsing origin: <> against <http://user:pass@example.org:21/smth>
-PASS Parsing origin: </some/path> against <http://user:pass@example.org:21/smth>
-PASS Parsing origin: <i> against <sc:/pa/pa>
-PASS Parsing origin: <i> against <sc://ho/pa>
-PASS Parsing origin: <i> against <sc:///pa/pa>
-PASS Parsing origin: <../i> against <sc:/pa/pa>
-PASS Parsing origin: <../i> against <sc://ho/pa>
-PASS Parsing origin: <../i> against <sc:///pa/pa>
-PASS Parsing origin: </i> against <sc:/pa/pa>
-PASS Parsing origin: </i> against <sc://ho/pa>
-PASS Parsing origin: </i> against <sc:///pa/pa>
-PASS Parsing origin: <?i> against <sc:/pa/pa>
-PASS Parsing origin: <?i> against <sc://ho/pa>
-PASS Parsing origin: <?i> against <sc:///pa/pa>
-PASS Parsing origin: <#i> against <sc:sd>
-PASS Parsing origin: <#i> against <sc:sd/sd>
-PASS Parsing origin: <#i> against <sc:/pa/pa>
-PASS Parsing origin: <#i> against <sc://ho/pa>
-PASS Parsing origin: <#i> against <sc:///pa/pa>
-PASS Parsing origin: <about:/../> against <about:blank>
-PASS Parsing origin: <data:/../> against <about:blank>
-PASS Parsing origin: <javascript:/../> against <about:blank>
-PASS Parsing origin: <mailto:/../> against <about:blank>
-PASS Parsing origin: <sc://ñ.test/> against <about:blank>
-PASS Parsing origin: <x> against <sc://ñ>
-PASS Parsing origin: <sc:\../> against <about:blank>
-PASS Parsing origin: <sc::a@example.net> against <about:blank>
-PASS Parsing origin: <wow:%NBD> against <about:blank>
-PASS Parsing origin: <wow:%1G> against <about:blank>
-PASS Parsing origin: <wow:￿> against <about:blank>
-FAIL Parsing origin: <http://example.com/U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿?U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿> against <about:blank> assert_equals: origin expected "http://example.com" but got "null"
-FAIL Parsing origin: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: origin expected "http://\x1f!\"$&'()*+,-.;=_`{}~" but got "null"
-PASS Parsing origin: <sc://!"$&'()*+,-.;=_`{}~/> against <about:blank>
-PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
-PASS Parsing origin: <https://%e2%98%83> against <about:blank>
-PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
-PASS Parsing origin: <http://facebook.com/?foo=%7B%22abc%22> against <about:blank>
-PASS Parsing origin: <https://localhost:3000/jqueryui@1.2.3> against <about:blank>
-PASS Parsing origin: <h	t
-t\rp://h	o
-s\rt:9	0
-0\r0/p	a
-t\rh?q	u
-e\rry#f	r
-a\rg> against <about:blank>
-PASS Parsing origin: <?a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing origin: <??a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:> against <http://example.org/foo/bar>
-PASS Parsing origin: <sc:> against <https://example.org/foo/bar>
-PASS Parsing origin: <http://foo.bar/baz?qux#foobar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo"bar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
-PASS Parsing origin: <http://192.168.257> against <http://other.com/>
-PASS Parsing origin: <http://192.168.257.com> against <http://other.com/>
-PASS Parsing origin: <http://256> against <http://other.com/>
-PASS Parsing origin: <http://256.com> against <http://other.com/>
-PASS Parsing origin: <http://999999999> against <http://other.com/>
-PASS Parsing origin: <http://999999999.com> against <http://other.com/>
-PASS Parsing origin: <http://10000000000.com> against <http://other.com/>
-PASS Parsing origin: <http://4294967295> against <http://other.com/>
-PASS Parsing origin: <http://0xffffffff> against <http://other.com/>
-PASS Parsing origin: <http://256.256.256.256.256> against <http://other.com/>
-PASS Parsing origin: <https://0x.0x.0> against <about:blank>
-PASS Parsing origin: <asdf://%43%7C/> against <about:blank>
-PASS Parsing origin: <http://[1:0::]> against <http://example.net/>
-PASS Parsing origin: <sc://ñ> against <about:blank>
-PASS Parsing origin: <sc://ñ?x> against <about:blank>
-PASS Parsing origin: <sc://ñ#x> against <about:blank>
-PASS Parsing origin: <#x> against <sc://ñ>
-PASS Parsing origin: <?x> against <sc://ñ>
-PASS Parsing origin: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank>
-PASS Parsing origin: <telnet://user:pass@foobar.com:23/> against <about:blank>
-PASS Parsing origin: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank>
-PASS Parsing origin: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank>
-PASS Parsing origin: <rsync://foo@host:911/sup> against <about:blank>
-PASS Parsing origin: <git://github.com/foo/bar.git> against <about:blank>
-PASS Parsing origin: <irc://myserver.com:6999/channel?passwd> against <about:blank>
-PASS Parsing origin: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank>
-PASS Parsing origin: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank>
-PASS Parsing origin: <git+https://github.com/foo/bar> against <about:blank>
-PASS Parsing origin: <urn:ietf:rfc:2648> against <about:blank>
-PASS Parsing origin: <tag:joe@example.org,2001:foo/bar> against <about:blank>
-PASS Parsing origin: <non-special:cannot-be-a-base-url-\0~€> against <about:blank>
-PASS Parsing origin: <https://www.example.com/path{path.html?query'=query#fragment<fragment> against <about:blank>
-PASS Parsing origin: <https://user:pass[@foo/bar> against <http://example.org>
-PASS Parsing origin: <foo:// !"$%&'()*+,-.;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <wss:// !"$%&'()*+,-.;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <foo://joe: !"$%&'()*+,-.:;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <wss://joe: !"$%&'()*+,-.:;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <foo://!"$%&'()*+,-.;=_`{}~/> against <about:blank>
-FAIL Parsing origin: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: origin expected "wss://!\"$&'()*+,-.;=_`{}~" but got "null"
-PASS Parsing origin: <foo://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <wss://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <foo://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <wss://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <foo://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <wss://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-origin-xhtml-expected.txt
deleted file mode 100644
index e1738cb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ /dev/null
@@ -1,337 +0,0 @@
-This is a testharness.js-based test.
-Found 324 tests; 319 PASS, 5 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Loading data…
-PASS Parsing origin: <http://example	.
-org> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://user:pass@foo:21/bar;par?b#c> against <http://example.org/foo/bar>
-PASS Parsing origin: <https://test:@test> against <about:blank>
-PASS Parsing origin: <https://:@test> against <about:blank>
-PASS Parsing origin: <non-special://test:@test/x> against <about:blank>
-PASS Parsing origin: <non-special://:@test/x> against <about:blank>
-PASS Parsing origin: <http:foo.com> against <http://example.org/foo/bar>
-PASS Parsing origin: <	   :foo.com   
-> against <http://example.org/foo/bar>
-PASS Parsing origin: < foo.com  > against <http://example.org/foo/bar>
-PASS Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://f:
-/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <> against <http://example.org/foo/bar>
-PASS Parsing origin: <  	> against <http://example.org/foo/bar>
-PASS Parsing origin: <:foo.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <:foo.com\> against <http://example.org/foo/bar>
-PASS Parsing origin: <:> against <http://example.org/foo/bar>
-PASS Parsing origin: <:a> against <http://example.org/foo/bar>
-PASS Parsing origin: <:/> against <http://example.org/foo/bar>
-PASS Parsing origin: <:\> against <http://example.org/foo/bar>
-PASS Parsing origin: <:#> against <http://example.org/foo/bar>
-PASS Parsing origin: <#> against <http://example.org/foo/bar>
-PASS Parsing origin: <#/> against <http://example.org/foo/bar>
-PASS Parsing origin: <#\> against <http://example.org/foo/bar>
-PASS Parsing origin: <#;?> against <http://example.org/foo/bar>
-PASS Parsing origin: <?> against <http://example.org/foo/bar>
-PASS Parsing origin: </> against <http://example.org/foo/bar>
-PASS Parsing origin: <:23> against <http://example.org/foo/bar>
-PASS Parsing origin: </:23> against <http://example.org/foo/bar>
-PASS Parsing origin: <\x> against <http://example.org/foo/bar>
-PASS Parsing origin: <\\x\hello> against <http://example.org/foo/bar>
-PASS Parsing origin: <::> against <http://example.org/foo/bar>
-PASS Parsing origin: <::23> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo://> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://a:b@c:29/d> against <http://example.org/foo/bar>
-PASS Parsing origin: <http::@c:29> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://&a:foo(b]c@d:2/> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://::@c@d:2> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo.com:b@d/> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo.com/\@> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:\\foo.com\> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:\\a\b:c\d@foo.com\> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo:/> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo:/bar.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo://///////> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo://///////bar.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <foo:////://///> against <http://example.org/foo/bar>
-PASS Parsing origin: <c:/foo> against <http://example.org/foo/bar>
-PASS Parsing origin: <//foo/bar> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo/path;a??e#f#g> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo/abcd?efgh?ijkl> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://foo/abcd#foo?bar> against <http://example.org/foo/bar>
-PASS Parsing origin: <[61:24:74]:98> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:[61:27]/:foo> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[2001::1]> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[::127.0.0.1]> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>
-PASS Parsing origin: <http://[2001::1]:80> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftp:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <https:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <madeupscheme:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftps:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <gopher:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ws:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <wss:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <data:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <javascript:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <mailto:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftp:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <https:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <madeupscheme:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ftps:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <gopher:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <ws:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <wss:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <data:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <javascript:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: <mailto:example.com/> against <http://example.org/foo/bar>
-PASS Parsing origin: </a/b/c> against <http://example.org/foo/bar>
-PASS Parsing origin: </a/ /c> against <http://example.org/foo/bar>
-PASS Parsing origin: </a%2fc> against <http://example.org/foo/bar>
-PASS Parsing origin: </a/%2f/c> against <http://example.org/foo/bar>
-PASS Parsing origin: <#β> against <http://example.org/foo/bar>
-PASS Parsing origin: <data:text/html,test#test> against <http://example.org/foo/bar>
-PASS Parsing origin: <tel:1234567890> against <http://example.org/foo/bar>
-PASS Parsing origin: <ssh://example.com/foo/bar.git> against <http://example.org/>
-PASS Parsing origin: <http://example.com/././foo> against <about:blank>
-PASS Parsing origin: <http://example.com/./.foo> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/.> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/./> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/../> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/..bar> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/../ton> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar/../ton/../../a> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/../../..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/../../../ton> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/%2e> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/%2e%2> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank>
-PASS Parsing origin: <http://example.com////../..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar//../..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo/bar//..> against <about:blank>
-PASS Parsing origin: <http://example.com/foo> against <about:blank>
-PASS Parsing origin: <http://example.com/%20foo> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%2> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%2zbar> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%2©zbar> against <about:blank>
-PASS Parsing origin: <http://example.com/foo%41%7a> against <about:blank>
-PASS Parsing origin: <http://example.com/foo	‘%91> against <about:blank>
-FAIL Parsing origin: <http://example.com/foo%00%51> against <about:blank> assert_equals: origin expected "http://example.com" but got "null"
-PASS Parsing origin: <http://example.com/(%28:%3A%29)> against <about:blank>
-PASS Parsing origin: <http://example.com/%3A%3a%3C%3c> against <about:blank>
-PASS Parsing origin: <http://example.com/foo	bar> against <about:blank>
-PASS Parsing origin: <http://example.com\\foo\\bar> against <about:blank>
-PASS Parsing origin: <http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd> against <about:blank>
-PASS Parsing origin: <http://example.com/@asdf%40> against <about:blank>
-PASS Parsing origin: <http://example.com/你好你好> against <about:blank>
-PASS Parsing origin: <http://example.com/‥/foo> against <about:blank>
-PASS Parsing origin: <http://example.com//foo> against <about:blank>
-PASS Parsing origin: <http://example.com/‮/foo/‭/bar> against <about:blank>
-PASS Parsing origin: <http://www.google.com/foo?bar=baz#> against <about:blank>
-PASS Parsing origin: <http://www.google.com/foo?bar=baz# »> against <about:blank>
-PASS Parsing origin: <data:test# »> against <about:blank>
-PASS Parsing origin: <http://www.google.com> against <about:blank>
-PASS Parsing origin: <http://192.0x00A80001> against <about:blank>
-PASS Parsing origin: <http://www/foo%2Ehtml> against <about:blank>
-PASS Parsing origin: <http://www/foo/%2E/html> against <about:blank>
-PASS Parsing origin: <http://%25DOMAIN:foobar@foodomain.com/> against <about:blank>
-PASS Parsing origin: <http:\\www.google.com\foo> against <about:blank>
-PASS Parsing origin: <http://foo:80/> against <about:blank>
-PASS Parsing origin: <http://foo:81/> against <about:blank>
-PASS Parsing origin: <httpa://foo:80/> against <about:blank>
-PASS Parsing origin: <https://foo:443/> against <about:blank>
-PASS Parsing origin: <https://foo:80/> against <about:blank>
-PASS Parsing origin: <ftp://foo:21/> against <about:blank>
-PASS Parsing origin: <ftp://foo:80/> against <about:blank>
-PASS Parsing origin: <gopher://foo:70/> against <about:blank>
-PASS Parsing origin: <gopher://foo:443/> against <about:blank>
-PASS Parsing origin: <ws://foo:80/> against <about:blank>
-PASS Parsing origin: <ws://foo:81/> against <about:blank>
-PASS Parsing origin: <ws://foo:443/> against <about:blank>
-PASS Parsing origin: <ws://foo:815/> against <about:blank>
-PASS Parsing origin: <wss://foo:80/> against <about:blank>
-PASS Parsing origin: <wss://foo:81/> against <about:blank>
-PASS Parsing origin: <wss://foo:443/> against <about:blank>
-PASS Parsing origin: <wss://foo:815/> against <about:blank>
-PASS Parsing origin: <http:/example.com/> against <about:blank>
-PASS Parsing origin: <ftp:/example.com/> against <about:blank>
-PASS Parsing origin: <https:/example.com/> against <about:blank>
-PASS Parsing origin: <madeupscheme:/example.com/> against <about:blank>
-PASS Parsing origin: <ftps:/example.com/> against <about:blank>
-PASS Parsing origin: <gopher:/example.com/> against <about:blank>
-PASS Parsing origin: <ws:/example.com/> against <about:blank>
-PASS Parsing origin: <wss:/example.com/> against <about:blank>
-PASS Parsing origin: <data:/example.com/> against <about:blank>
-PASS Parsing origin: <javascript:/example.com/> against <about:blank>
-PASS Parsing origin: <mailto:/example.com/> against <about:blank>
-PASS Parsing origin: <http:example.com/> against <about:blank>
-PASS Parsing origin: <ftp:example.com/> against <about:blank>
-PASS Parsing origin: <https:example.com/> against <about:blank>
-PASS Parsing origin: <madeupscheme:example.com/> against <about:blank>
-PASS Parsing origin: <ftps:example.com/> against <about:blank>
-PASS Parsing origin: <gopher:example.com/> against <about:blank>
-PASS Parsing origin: <ws:example.com/> against <about:blank>
-PASS Parsing origin: <wss:example.com/> against <about:blank>
-PASS Parsing origin: <data:example.com/> against <about:blank>
-PASS Parsing origin: <javascript:example.com/> against <about:blank>
-PASS Parsing origin: <mailto:example.com/> against <about:blank>
-PASS Parsing origin: <http:@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/@www.example.com> against <about:blank>
-PASS Parsing origin: <http://@www.example.com> against <about:blank>
-PASS Parsing origin: <http:a:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/a:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http://a:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http://@pple.com> against <about:blank>
-PASS Parsing origin: <http::b@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http://:b@www.example.com> against <about:blank>
-PASS Parsing origin: <http:a:@www.example.com> against <about:blank>
-PASS Parsing origin: <http:/a:@www.example.com> against <about:blank>
-PASS Parsing origin: <http://a:@www.example.com> against <about:blank>
-PASS Parsing origin: <http://www.@pple.com> against <about:blank>
-PASS Parsing origin: <http://:@www.example.com> against <about:blank>
-PASS Parsing origin: </> against <http://www.example.com/test>
-PASS Parsing origin: </test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <.> against <http://www.example.com/test>
-PASS Parsing origin: <..> against <http://www.example.com/test>
-PASS Parsing origin: <test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <./test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <../test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <../aaa/test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <../../test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <中/test.txt> against <http://www.example.com/test>
-PASS Parsing origin: <http://www.example2.com> against <http://www.example.com/test>
-PASS Parsing origin: <//www.example2.com> against <http://www.example.com/test>
-PASS Parsing origin: <http://ExAmPlE.CoM> against <http://other.com/>
-PASS Parsing origin: <http://GOO​⁠goo.com> against <http://other.com/>
-PASS Parsing origin: <\0 http://example.com/ \r > against <about:blank>
-PASS Parsing origin: <http://www.foo。bar.com> against <http://other.com/>
-PASS Parsing origin: <https://x/�?�#�> against <about:blank>
-PASS Parsing origin: <http://Go.com> against <http://other.com/>
-PASS Parsing origin: <http://你好你好> against <http://other.com/>
-FAIL Parsing origin: <https://faß.ExAmPlE/> against <about:blank> assert_equals: origin expected "https://xn--fa-hia.example" but got "https://fass.example"
-PASS Parsing origin: <sc://faß.ExAmPlE/> against <about:blank>
-PASS Parsing origin: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>
-PASS Parsing origin: <http://%30%78%63%30%2e%30%32%35%30.01%2e> against <http://other.com/>
-PASS Parsing origin: <http://0Xc0.0250.01> against <http://other.com/>
-PASS Parsing origin: <http://./> against <about:blank>
-PASS Parsing origin: <http://../> against <about:blank>
-PASS Parsing origin: <http://0..0x300/> against <about:blank>
-PASS Parsing origin: <http://foo:💩@example.com/bar> against <http://other.com/>
-PASS Parsing origin: <#> against <test:test>
-PASS Parsing origin: <#x> against <mailto:x@x.com>
-PASS Parsing origin: <#x> against <data:,>
-PASS Parsing origin: <#x> against <about:blank>
-PASS Parsing origin: <#> against <test:test?test>
-PASS Parsing origin: <https://@test@test@example:800/> against <http://doesnotmatter/>
-PASS Parsing origin: <https://@@@example> against <http://doesnotmatter/>
-PASS Parsing origin: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>
-PASS Parsing origin: <http://host/?'> against <about:blank>
-PASS Parsing origin: <notspecial://host/?'> against <about:blank>
-PASS Parsing origin: </some/path> against <http://user@example.org/smth>
-PASS Parsing origin: <> against <http://user:pass@example.org:21/smth>
-PASS Parsing origin: </some/path> against <http://user:pass@example.org:21/smth>
-PASS Parsing origin: <i> against <sc:/pa/pa>
-PASS Parsing origin: <i> against <sc://ho/pa>
-PASS Parsing origin: <i> against <sc:///pa/pa>
-PASS Parsing origin: <../i> against <sc:/pa/pa>
-PASS Parsing origin: <../i> against <sc://ho/pa>
-PASS Parsing origin: <../i> against <sc:///pa/pa>
-PASS Parsing origin: </i> against <sc:/pa/pa>
-PASS Parsing origin: </i> against <sc://ho/pa>
-PASS Parsing origin: </i> against <sc:///pa/pa>
-PASS Parsing origin: <?i> against <sc:/pa/pa>
-PASS Parsing origin: <?i> against <sc://ho/pa>
-PASS Parsing origin: <?i> against <sc:///pa/pa>
-PASS Parsing origin: <#i> against <sc:sd>
-PASS Parsing origin: <#i> against <sc:sd/sd>
-PASS Parsing origin: <#i> against <sc:/pa/pa>
-PASS Parsing origin: <#i> against <sc://ho/pa>
-PASS Parsing origin: <#i> against <sc:///pa/pa>
-PASS Parsing origin: <about:/../> against <about:blank>
-PASS Parsing origin: <data:/../> against <about:blank>
-PASS Parsing origin: <javascript:/../> against <about:blank>
-PASS Parsing origin: <mailto:/../> against <about:blank>
-PASS Parsing origin: <sc://ñ.test/> against <about:blank>
-PASS Parsing origin: <x> against <sc://ñ>
-PASS Parsing origin: <sc:\../> against <about:blank>
-PASS Parsing origin: <sc::a@example.net> against <about:blank>
-PASS Parsing origin: <wow:%NBD> against <about:blank>
-PASS Parsing origin: <wow:%1G> against <about:blank>
-PASS Parsing origin: <wow:￿> against <about:blank>
-FAIL Parsing origin: <http://example.com/U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿?U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿> against <about:blank> assert_equals: origin expected "http://example.com" but got "null"
-FAIL Parsing origin: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: origin expected "http://\x1f!\"$&'()*+,-.;=_`{}~" but got "null"
-PASS Parsing origin: <sc://!"$&'()*+,-.;=_`{}~/> against <about:blank>
-PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
-PASS Parsing origin: <https://%e2%98%83> against <about:blank>
-PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
-PASS Parsing origin: <http://facebook.com/?foo=%7B%22abc%22> against <about:blank>
-PASS Parsing origin: <https://localhost:3000/jqueryui@1.2.3> against <about:blank>
-PASS Parsing origin: <h	t
-t\rp://h	o
-s\rt:9	0
-0\r0/p	a
-t\rh?q	u
-e\rry#f	r
-a\rg> against <about:blank>
-PASS Parsing origin: <?a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing origin: <??a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing origin: <http:> against <http://example.org/foo/bar>
-PASS Parsing origin: <sc:> against <https://example.org/foo/bar>
-PASS Parsing origin: <http://foo.bar/baz?qux#foobar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo"bar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
-PASS Parsing origin: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
-PASS Parsing origin: <http://192.168.257> against <http://other.com/>
-PASS Parsing origin: <http://192.168.257.com> against <http://other.com/>
-PASS Parsing origin: <http://256> against <http://other.com/>
-PASS Parsing origin: <http://256.com> against <http://other.com/>
-PASS Parsing origin: <http://999999999> against <http://other.com/>
-PASS Parsing origin: <http://999999999.com> against <http://other.com/>
-PASS Parsing origin: <http://10000000000.com> against <http://other.com/>
-PASS Parsing origin: <http://4294967295> against <http://other.com/>
-PASS Parsing origin: <http://0xffffffff> against <http://other.com/>
-PASS Parsing origin: <http://256.256.256.256.256> against <http://other.com/>
-PASS Parsing origin: <https://0x.0x.0> against <about:blank>
-PASS Parsing origin: <asdf://%43%7C/> against <about:blank>
-PASS Parsing origin: <http://[1:0::]> against <http://example.net/>
-PASS Parsing origin: <sc://ñ> against <about:blank>
-PASS Parsing origin: <sc://ñ?x> against <about:blank>
-PASS Parsing origin: <sc://ñ#x> against <about:blank>
-PASS Parsing origin: <#x> against <sc://ñ>
-PASS Parsing origin: <?x> against <sc://ñ>
-PASS Parsing origin: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank>
-PASS Parsing origin: <telnet://user:pass@foobar.com:23/> against <about:blank>
-PASS Parsing origin: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank>
-PASS Parsing origin: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank>
-PASS Parsing origin: <rsync://foo@host:911/sup> against <about:blank>
-PASS Parsing origin: <git://github.com/foo/bar.git> against <about:blank>
-PASS Parsing origin: <irc://myserver.com:6999/channel?passwd> against <about:blank>
-PASS Parsing origin: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank>
-PASS Parsing origin: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank>
-PASS Parsing origin: <git+https://github.com/foo/bar> against <about:blank>
-PASS Parsing origin: <urn:ietf:rfc:2648> against <about:blank>
-PASS Parsing origin: <tag:joe@example.org,2001:foo/bar> against <about:blank>
-PASS Parsing origin: <non-special:cannot-be-a-base-url-\0~€> against <about:blank>
-PASS Parsing origin: <https://www.example.com/path{path.html?query'=query#fragment<fragment> against <about:blank>
-PASS Parsing origin: <https://user:pass[@foo/bar> against <http://example.org>
-PASS Parsing origin: <foo:// !"$%&'()*+,-.;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <wss:// !"$%&'()*+,-.;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <foo://joe: !"$%&'()*+,-.:;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <wss://joe: !"$%&'()*+,-.:;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Parsing origin: <foo://!"$%&'()*+,-.;=_`{}~/> against <about:blank>
-FAIL Parsing origin: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: origin expected "wss://!\"$&'()*+,-.;=_`{}~" but got "null"
-PASS Parsing origin: <foo://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <wss://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <foo://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <wss://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <foo://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Parsing origin: <wss://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/url-origin.any-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/url-origin.any-expected.txt
deleted file mode 100644
index dac7874..0000000
--- a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/url-origin.any-expected.txt
+++ /dev/null
@@ -1,337 +0,0 @@
-This is a testharness.js-based test.
-Found 324 tests; 316 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Loading data…
-PASS Origin parsing: <http://example	.
-org> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://user:pass@foo:21/bar;par?b#c> against <http://example.org/foo/bar>
-PASS Origin parsing: <https://test:@test> against <about:blank>
-PASS Origin parsing: <https://:@test> against <about:blank>
-PASS Origin parsing: <non-special://test:@test/x> against <about:blank>
-PASS Origin parsing: <non-special://:@test/x> against <about:blank>
-PASS Origin parsing: <http:foo.com> against <http://example.org/foo/bar>
-PASS Origin parsing: <	   :foo.com   
-> against <http://example.org/foo/bar>
-PASS Origin parsing: < foo.com  > against <http://example.org/foo/bar>
-PASS Origin parsing: <a:	 foo.com> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:0/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:
-/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <> against <http://example.org/foo/bar>
-PASS Origin parsing: <  	> against <http://example.org/foo/bar>
-PASS Origin parsing: <:foo.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <:foo.com\> against <http://example.org/foo/bar>
-PASS Origin parsing: <:> against <http://example.org/foo/bar>
-PASS Origin parsing: <:a> against <http://example.org/foo/bar>
-PASS Origin parsing: <:/> against <http://example.org/foo/bar>
-PASS Origin parsing: <:\> against <http://example.org/foo/bar>
-PASS Origin parsing: <:#> against <http://example.org/foo/bar>
-PASS Origin parsing: <#> against <http://example.org/foo/bar>
-PASS Origin parsing: <#/> against <http://example.org/foo/bar>
-PASS Origin parsing: <#\> against <http://example.org/foo/bar>
-PASS Origin parsing: <#;?> against <http://example.org/foo/bar>
-PASS Origin parsing: <?> against <http://example.org/foo/bar>
-PASS Origin parsing: </> against <http://example.org/foo/bar>
-PASS Origin parsing: <:23> against <http://example.org/foo/bar>
-PASS Origin parsing: </:23> against <http://example.org/foo/bar>
-PASS Origin parsing: <\x> against <http://example.org/foo/bar>
-PASS Origin parsing: <\\x\hello> against <http://example.org/foo/bar>
-PASS Origin parsing: <::> against <http://example.org/foo/bar>
-PASS Origin parsing: <::23> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo://> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://a:b@c:29/d> against <http://example.org/foo/bar>
-PASS Origin parsing: <http::@c:29> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://&a:foo(b]c@d:2/> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://::@c@d:2> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo.com/\@> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:\\foo.com\> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:\\a\b:c\d@foo.com\> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo:/> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo:/bar.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo://///////> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo://///////bar.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo:////://///> against <http://example.org/foo/bar>
-PASS Origin parsing: <c:/foo> against <http://example.org/foo/bar>
-PASS Origin parsing: <//foo/bar> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo/path;a??e#f#g> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo/abcd?efgh?ijkl> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo/abcd#foo?bar> against <http://example.org/foo/bar>
-PASS Origin parsing: <[61:24:74]:98> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:[61:27]/:foo> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[2001::1]> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[::127.0.0.1]> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[2001::1]:80> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftp:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <https:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <madeupscheme:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftps:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <gopher:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ws:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <wss:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <data:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <javascript:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <mailto:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftp:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <https:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <madeupscheme:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftps:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <gopher:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ws:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <wss:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <data:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <javascript:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <mailto:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: </a/b/c> against <http://example.org/foo/bar>
-PASS Origin parsing: </a/ /c> against <http://example.org/foo/bar>
-PASS Origin parsing: </a%2fc> against <http://example.org/foo/bar>
-PASS Origin parsing: </a/%2f/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <#β> against <http://example.org/foo/bar>
-PASS Origin parsing: <data:text/html,test#test> against <http://example.org/foo/bar>
-PASS Origin parsing: <tel:1234567890> against <http://example.org/foo/bar>
-PASS Origin parsing: <ssh://example.com/foo/bar.git> against <http://example.org/>
-PASS Origin parsing: <http://example.com/././foo> against <about:blank>
-PASS Origin parsing: <http://example.com/./.foo> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/.> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/./> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/../> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/..bar> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/../ton> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/../ton/../../a> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/../../..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/../../../ton> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/%2e> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/%2e%2> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank>
-PASS Origin parsing: <http://example.com////../..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar//../..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar//..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo> against <about:blank>
-PASS Origin parsing: <http://example.com/%20foo> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%2> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%2zbar> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%2©zbar> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%41%7a> against <about:blank>
-PASS Origin parsing: <http://example.com/foo	‘%91> against <about:blank>
-FAIL Origin parsing: <http://example.com/foo%00%51> against <about:blank> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <http://example.com/(%28:%3A%29)> against <about:blank>
-PASS Origin parsing: <http://example.com/%3A%3a%3C%3c> against <about:blank>
-PASS Origin parsing: <http://example.com/foo	bar> against <about:blank>
-PASS Origin parsing: <http://example.com\\foo\\bar> against <about:blank>
-PASS Origin parsing: <http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd> against <about:blank>
-PASS Origin parsing: <http://example.com/@asdf%40> against <about:blank>
-PASS Origin parsing: <http://example.com/你好你好> against <about:blank>
-PASS Origin parsing: <http://example.com/‥/foo> against <about:blank>
-PASS Origin parsing: <http://example.com//foo> against <about:blank>
-PASS Origin parsing: <http://example.com/‮/foo/‭/bar> against <about:blank>
-PASS Origin parsing: <http://www.google.com/foo?bar=baz#> against <about:blank>
-PASS Origin parsing: <http://www.google.com/foo?bar=baz# »> against <about:blank>
-PASS Origin parsing: <data:test# »> against <about:blank>
-PASS Origin parsing: <http://www.google.com> against <about:blank>
-PASS Origin parsing: <http://192.0x00A80001> against <about:blank>
-PASS Origin parsing: <http://www/foo%2Ehtml> against <about:blank>
-PASS Origin parsing: <http://www/foo/%2E/html> against <about:blank>
-PASS Origin parsing: <http://%25DOMAIN:foobar@foodomain.com/> against <about:blank>
-PASS Origin parsing: <http:\\www.google.com\foo> against <about:blank>
-PASS Origin parsing: <http://foo:80/> against <about:blank>
-PASS Origin parsing: <http://foo:81/> against <about:blank>
-PASS Origin parsing: <httpa://foo:80/> against <about:blank>
-PASS Origin parsing: <https://foo:443/> against <about:blank>
-PASS Origin parsing: <https://foo:80/> against <about:blank>
-PASS Origin parsing: <ftp://foo:21/> against <about:blank>
-PASS Origin parsing: <ftp://foo:80/> against <about:blank>
-PASS Origin parsing: <gopher://foo:70/> against <about:blank>
-PASS Origin parsing: <gopher://foo:443/> against <about:blank>
-PASS Origin parsing: <ws://foo:80/> against <about:blank>
-PASS Origin parsing: <ws://foo:81/> against <about:blank>
-PASS Origin parsing: <ws://foo:443/> against <about:blank>
-PASS Origin parsing: <ws://foo:815/> against <about:blank>
-PASS Origin parsing: <wss://foo:80/> against <about:blank>
-PASS Origin parsing: <wss://foo:81/> against <about:blank>
-PASS Origin parsing: <wss://foo:443/> against <about:blank>
-PASS Origin parsing: <wss://foo:815/> against <about:blank>
-PASS Origin parsing: <http:/example.com/> against <about:blank>
-PASS Origin parsing: <ftp:/example.com/> against <about:blank>
-PASS Origin parsing: <https:/example.com/> against <about:blank>
-PASS Origin parsing: <madeupscheme:/example.com/> against <about:blank>
-PASS Origin parsing: <ftps:/example.com/> against <about:blank>
-PASS Origin parsing: <gopher:/example.com/> against <about:blank>
-PASS Origin parsing: <ws:/example.com/> against <about:blank>
-PASS Origin parsing: <wss:/example.com/> against <about:blank>
-PASS Origin parsing: <data:/example.com/> against <about:blank>
-PASS Origin parsing: <javascript:/example.com/> against <about:blank>
-PASS Origin parsing: <mailto:/example.com/> against <about:blank>
-PASS Origin parsing: <http:example.com/> against <about:blank>
-PASS Origin parsing: <ftp:example.com/> against <about:blank>
-PASS Origin parsing: <https:example.com/> against <about:blank>
-PASS Origin parsing: <madeupscheme:example.com/> against <about:blank>
-PASS Origin parsing: <ftps:example.com/> against <about:blank>
-PASS Origin parsing: <gopher:example.com/> against <about:blank>
-PASS Origin parsing: <ws:example.com/> against <about:blank>
-PASS Origin parsing: <wss:example.com/> against <about:blank>
-PASS Origin parsing: <data:example.com/> against <about:blank>
-PASS Origin parsing: <javascript:example.com/> against <about:blank>
-PASS Origin parsing: <mailto:example.com/> against <about:blank>
-PASS Origin parsing: <http:@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/@www.example.com> against <about:blank>
-PASS Origin parsing: <http://@www.example.com> against <about:blank>
-PASS Origin parsing: <http:a:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/a:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http://a:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http://@pple.com> against <about:blank>
-PASS Origin parsing: <http::b@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http://:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http:a:@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/a:@www.example.com> against <about:blank>
-PASS Origin parsing: <http://a:@www.example.com> against <about:blank>
-PASS Origin parsing: <http://www.@pple.com> against <about:blank>
-PASS Origin parsing: <http://:@www.example.com> against <about:blank>
-PASS Origin parsing: </> against <http://www.example.com/test>
-PASS Origin parsing: </test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <.> against <http://www.example.com/test>
-PASS Origin parsing: <..> against <http://www.example.com/test>
-PASS Origin parsing: <test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <./test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <../test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <../aaa/test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <../../test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <中/test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <http://www.example2.com> against <http://www.example.com/test>
-PASS Origin parsing: <//www.example2.com> against <http://www.example.com/test>
-PASS Origin parsing: <http://ExAmPlE.CoM> against <http://other.com/>
-PASS Origin parsing: <http://GOO​⁠goo.com> against <http://other.com/>
-PASS Origin parsing: <\0 http://example.com/ \r > against <about:blank>
-PASS Origin parsing: <http://www.foo。bar.com> against <http://other.com/>
-PASS Origin parsing: <https://x/�?�#�> against <about:blank>
-PASS Origin parsing: <http://Go.com> against <http://other.com/>
-PASS Origin parsing: <http://你好你好> against <http://other.com/>
-FAIL Origin parsing: <https://faß.ExAmPlE/> against <about:blank> assert_equals: origin expected "https://xn--fa-hia.example" but got "https://fass.example"
-PASS Origin parsing: <sc://faß.ExAmPlE/> against <about:blank>
-PASS Origin parsing: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>
-PASS Origin parsing: <http://%30%78%63%30%2e%30%32%35%30.01%2e> against <http://other.com/>
-PASS Origin parsing: <http://0Xc0.0250.01> against <http://other.com/>
-PASS Origin parsing: <http://./> against <about:blank>
-PASS Origin parsing: <http://../> against <about:blank>
-PASS Origin parsing: <http://0..0x300/> against <about:blank>
-PASS Origin parsing: <http://foo:💩@example.com/bar> against <http://other.com/>
-PASS Origin parsing: <#> against <test:test>
-PASS Origin parsing: <#x> against <mailto:x@x.com>
-PASS Origin parsing: <#x> against <data:,>
-PASS Origin parsing: <#x> against <about:blank>
-PASS Origin parsing: <#> against <test:test?test>
-PASS Origin parsing: <https://@test@test@example:800/> against <http://doesnotmatter/>
-PASS Origin parsing: <https://@@@example> against <http://doesnotmatter/>
-PASS Origin parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>
-PASS Origin parsing: <http://host/?'> against <about:blank>
-PASS Origin parsing: <notspecial://host/?'> against <about:blank>
-PASS Origin parsing: </some/path> against <http://user@example.org/smth>
-PASS Origin parsing: <> against <http://user:pass@example.org:21/smth>
-PASS Origin parsing: </some/path> against <http://user:pass@example.org:21/smth>
-PASS Origin parsing: <i> against <sc:/pa/pa>
-PASS Origin parsing: <i> against <sc://ho/pa>
-PASS Origin parsing: <i> against <sc:///pa/pa>
-PASS Origin parsing: <../i> against <sc:/pa/pa>
-PASS Origin parsing: <../i> against <sc://ho/pa>
-PASS Origin parsing: <../i> against <sc:///pa/pa>
-PASS Origin parsing: </i> against <sc:/pa/pa>
-PASS Origin parsing: </i> against <sc://ho/pa>
-PASS Origin parsing: </i> against <sc:///pa/pa>
-PASS Origin parsing: <?i> against <sc:/pa/pa>
-PASS Origin parsing: <?i> against <sc://ho/pa>
-PASS Origin parsing: <?i> against <sc:///pa/pa>
-PASS Origin parsing: <#i> against <sc:sd>
-PASS Origin parsing: <#i> against <sc:sd/sd>
-PASS Origin parsing: <#i> against <sc:/pa/pa>
-PASS Origin parsing: <#i> against <sc://ho/pa>
-PASS Origin parsing: <#i> against <sc:///pa/pa>
-PASS Origin parsing: <about:/../> against <about:blank>
-PASS Origin parsing: <data:/../> against <about:blank>
-PASS Origin parsing: <javascript:/../> against <about:blank>
-PASS Origin parsing: <mailto:/../> against <about:blank>
-PASS Origin parsing: <sc://ñ.test/> against <about:blank>
-FAIL Origin parsing: <x> against <sc://ñ> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <sc:\../> against <about:blank>
-PASS Origin parsing: <sc::a@example.net> against <about:blank>
-PASS Origin parsing: <wow:%NBD> against <about:blank>
-PASS Origin parsing: <wow:%1G> against <about:blank>
-PASS Origin parsing: <wow:￿> against <about:blank>
-FAIL Origin parsing: <http://example.com/U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿?U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿> against <about:blank> Failed to construct 'URL': Invalid URL
-FAIL Origin parsing: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <sc://!"$&'()*+,-.;=_`{}~/> against <about:blank>
-PASS Origin parsing: <ftp://%e2%98%83> against <about:blank>
-PASS Origin parsing: <https://%e2%98%83> against <about:blank>
-PASS Origin parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
-PASS Origin parsing: <http://facebook.com/?foo=%7B%22abc%22> against <about:blank>
-PASS Origin parsing: <https://localhost:3000/jqueryui@1.2.3> against <about:blank>
-PASS Origin parsing: <h	t
-t\rp://h	o
-s\rt:9	0
-0\r0/p	a
-t\rh?q	u
-e\rry#f	r
-a\rg> against <about:blank>
-PASS Origin parsing: <?a=b&c=d> against <http://example.org/foo/bar>
-PASS Origin parsing: <??a=b&c=d> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:> against <http://example.org/foo/bar>
-PASS Origin parsing: <sc:> against <https://example.org/foo/bar>
-PASS Origin parsing: <http://foo.bar/baz?qux#foobar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo"bar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
-PASS Origin parsing: <http://192.168.257> against <http://other.com/>
-PASS Origin parsing: <http://192.168.257.com> against <http://other.com/>
-PASS Origin parsing: <http://256> against <http://other.com/>
-PASS Origin parsing: <http://256.com> against <http://other.com/>
-PASS Origin parsing: <http://999999999> against <http://other.com/>
-PASS Origin parsing: <http://999999999.com> against <http://other.com/>
-PASS Origin parsing: <http://10000000000.com> against <http://other.com/>
-PASS Origin parsing: <http://4294967295> against <http://other.com/>
-PASS Origin parsing: <http://0xffffffff> against <http://other.com/>
-PASS Origin parsing: <http://256.256.256.256.256> against <http://other.com/>
-PASS Origin parsing: <https://0x.0x.0> against <about:blank>
-PASS Origin parsing: <asdf://%43%7C/> against <about:blank>
-PASS Origin parsing: <http://[1:0::]> against <http://example.net/>
-PASS Origin parsing: <sc://ñ> against <about:blank>
-PASS Origin parsing: <sc://ñ?x> against <about:blank>
-PASS Origin parsing: <sc://ñ#x> against <about:blank>
-FAIL Origin parsing: <#x> against <sc://ñ> Failed to construct 'URL': Invalid URL
-FAIL Origin parsing: <?x> against <sc://ñ> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank>
-PASS Origin parsing: <telnet://user:pass@foobar.com:23/> against <about:blank>
-PASS Origin parsing: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank>
-PASS Origin parsing: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank>
-PASS Origin parsing: <rsync://foo@host:911/sup> against <about:blank>
-PASS Origin parsing: <git://github.com/foo/bar.git> against <about:blank>
-PASS Origin parsing: <irc://myserver.com:6999/channel?passwd> against <about:blank>
-PASS Origin parsing: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank>
-PASS Origin parsing: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank>
-PASS Origin parsing: <git+https://github.com/foo/bar> against <about:blank>
-PASS Origin parsing: <urn:ietf:rfc:2648> against <about:blank>
-PASS Origin parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
-PASS Origin parsing: <non-special:cannot-be-a-base-url-\0~€> against <about:blank>
-PASS Origin parsing: <https://www.example.com/path{path.html?query'=query#fragment<fragment> against <about:blank>
-PASS Origin parsing: <https://user:pass[@foo/bar> against <http://example.org>
-PASS Origin parsing: <foo:// !"$%&'()*+,-.;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <wss:// !"$%&'()*+,-.;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <foo://joe: !"$%&'()*+,-.:;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <wss://joe: !"$%&'()*+,-.:;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <foo://!"$%&'()*+,-.;=_`{}~/> against <about:blank>
-FAIL Origin parsing: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <foo://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <wss://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <foo://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <wss://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <foo://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <wss://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/url-origin.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/url-origin.any.worker-expected.txt
deleted file mode 100644
index dac7874..0000000
--- a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/url/url-origin.any.worker-expected.txt
+++ /dev/null
@@ -1,337 +0,0 @@
-This is a testharness.js-based test.
-Found 324 tests; 316 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Loading data…
-PASS Origin parsing: <http://example	.
-org> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://user:pass@foo:21/bar;par?b#c> against <http://example.org/foo/bar>
-PASS Origin parsing: <https://test:@test> against <about:blank>
-PASS Origin parsing: <https://:@test> against <about:blank>
-PASS Origin parsing: <non-special://test:@test/x> against <about:blank>
-PASS Origin parsing: <non-special://:@test/x> against <about:blank>
-PASS Origin parsing: <http:foo.com> against <http://example.org/foo/bar>
-PASS Origin parsing: <	   :foo.com   
-> against <http://example.org/foo/bar>
-PASS Origin parsing: < foo.com  > against <http://example.org/foo/bar>
-PASS Origin parsing: <a:	 foo.com> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:0/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://f:
-/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <> against <http://example.org/foo/bar>
-PASS Origin parsing: <  	> against <http://example.org/foo/bar>
-PASS Origin parsing: <:foo.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <:foo.com\> against <http://example.org/foo/bar>
-PASS Origin parsing: <:> against <http://example.org/foo/bar>
-PASS Origin parsing: <:a> against <http://example.org/foo/bar>
-PASS Origin parsing: <:/> against <http://example.org/foo/bar>
-PASS Origin parsing: <:\> against <http://example.org/foo/bar>
-PASS Origin parsing: <:#> against <http://example.org/foo/bar>
-PASS Origin parsing: <#> against <http://example.org/foo/bar>
-PASS Origin parsing: <#/> against <http://example.org/foo/bar>
-PASS Origin parsing: <#\> against <http://example.org/foo/bar>
-PASS Origin parsing: <#;?> against <http://example.org/foo/bar>
-PASS Origin parsing: <?> against <http://example.org/foo/bar>
-PASS Origin parsing: </> against <http://example.org/foo/bar>
-PASS Origin parsing: <:23> against <http://example.org/foo/bar>
-PASS Origin parsing: </:23> against <http://example.org/foo/bar>
-PASS Origin parsing: <\x> against <http://example.org/foo/bar>
-PASS Origin parsing: <\\x\hello> against <http://example.org/foo/bar>
-PASS Origin parsing: <::> against <http://example.org/foo/bar>
-PASS Origin parsing: <::23> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo://> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://a:b@c:29/d> against <http://example.org/foo/bar>
-PASS Origin parsing: <http::@c:29> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://&a:foo(b]c@d:2/> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://::@c@d:2> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo.com/\@> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:\\foo.com\> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:\\a\b:c\d@foo.com\> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo:/> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo:/bar.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo://///////> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo://///////bar.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <foo:////://///> against <http://example.org/foo/bar>
-PASS Origin parsing: <c:/foo> against <http://example.org/foo/bar>
-PASS Origin parsing: <//foo/bar> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo/path;a??e#f#g> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo/abcd?efgh?ijkl> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://foo/abcd#foo?bar> against <http://example.org/foo/bar>
-PASS Origin parsing: <[61:24:74]:98> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:[61:27]/:foo> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[2001::1]> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[::127.0.0.1]> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>
-PASS Origin parsing: <http://[2001::1]:80> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftp:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <https:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <madeupscheme:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftps:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <gopher:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ws:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <wss:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <data:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <javascript:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <mailto:/example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftp:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <https:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <madeupscheme:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ftps:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <gopher:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <ws:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <wss:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <data:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <javascript:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: <mailto:example.com/> against <http://example.org/foo/bar>
-PASS Origin parsing: </a/b/c> against <http://example.org/foo/bar>
-PASS Origin parsing: </a/ /c> against <http://example.org/foo/bar>
-PASS Origin parsing: </a%2fc> against <http://example.org/foo/bar>
-PASS Origin parsing: </a/%2f/c> against <http://example.org/foo/bar>
-PASS Origin parsing: <#β> against <http://example.org/foo/bar>
-PASS Origin parsing: <data:text/html,test#test> against <http://example.org/foo/bar>
-PASS Origin parsing: <tel:1234567890> against <http://example.org/foo/bar>
-PASS Origin parsing: <ssh://example.com/foo/bar.git> against <http://example.org/>
-PASS Origin parsing: <http://example.com/././foo> against <about:blank>
-PASS Origin parsing: <http://example.com/./.foo> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/.> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/./> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/../> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/..bar> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/../ton> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar/../ton/../../a> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/../../..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/../../../ton> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/%2e> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/%2e%2> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank>
-PASS Origin parsing: <http://example.com////../..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar//../..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo/bar//..> against <about:blank>
-PASS Origin parsing: <http://example.com/foo> against <about:blank>
-PASS Origin parsing: <http://example.com/%20foo> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%2> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%2zbar> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%2©zbar> against <about:blank>
-PASS Origin parsing: <http://example.com/foo%41%7a> against <about:blank>
-PASS Origin parsing: <http://example.com/foo	‘%91> against <about:blank>
-FAIL Origin parsing: <http://example.com/foo%00%51> against <about:blank> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <http://example.com/(%28:%3A%29)> against <about:blank>
-PASS Origin parsing: <http://example.com/%3A%3a%3C%3c> against <about:blank>
-PASS Origin parsing: <http://example.com/foo	bar> against <about:blank>
-PASS Origin parsing: <http://example.com\\foo\\bar> against <about:blank>
-PASS Origin parsing: <http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd> against <about:blank>
-PASS Origin parsing: <http://example.com/@asdf%40> against <about:blank>
-PASS Origin parsing: <http://example.com/你好你好> against <about:blank>
-PASS Origin parsing: <http://example.com/‥/foo> against <about:blank>
-PASS Origin parsing: <http://example.com//foo> against <about:blank>
-PASS Origin parsing: <http://example.com/‮/foo/‭/bar> against <about:blank>
-PASS Origin parsing: <http://www.google.com/foo?bar=baz#> against <about:blank>
-PASS Origin parsing: <http://www.google.com/foo?bar=baz# »> against <about:blank>
-PASS Origin parsing: <data:test# »> against <about:blank>
-PASS Origin parsing: <http://www.google.com> against <about:blank>
-PASS Origin parsing: <http://192.0x00A80001> against <about:blank>
-PASS Origin parsing: <http://www/foo%2Ehtml> against <about:blank>
-PASS Origin parsing: <http://www/foo/%2E/html> against <about:blank>
-PASS Origin parsing: <http://%25DOMAIN:foobar@foodomain.com/> against <about:blank>
-PASS Origin parsing: <http:\\www.google.com\foo> against <about:blank>
-PASS Origin parsing: <http://foo:80/> against <about:blank>
-PASS Origin parsing: <http://foo:81/> against <about:blank>
-PASS Origin parsing: <httpa://foo:80/> against <about:blank>
-PASS Origin parsing: <https://foo:443/> against <about:blank>
-PASS Origin parsing: <https://foo:80/> against <about:blank>
-PASS Origin parsing: <ftp://foo:21/> against <about:blank>
-PASS Origin parsing: <ftp://foo:80/> against <about:blank>
-PASS Origin parsing: <gopher://foo:70/> against <about:blank>
-PASS Origin parsing: <gopher://foo:443/> against <about:blank>
-PASS Origin parsing: <ws://foo:80/> against <about:blank>
-PASS Origin parsing: <ws://foo:81/> against <about:blank>
-PASS Origin parsing: <ws://foo:443/> against <about:blank>
-PASS Origin parsing: <ws://foo:815/> against <about:blank>
-PASS Origin parsing: <wss://foo:80/> against <about:blank>
-PASS Origin parsing: <wss://foo:81/> against <about:blank>
-PASS Origin parsing: <wss://foo:443/> against <about:blank>
-PASS Origin parsing: <wss://foo:815/> against <about:blank>
-PASS Origin parsing: <http:/example.com/> against <about:blank>
-PASS Origin parsing: <ftp:/example.com/> against <about:blank>
-PASS Origin parsing: <https:/example.com/> against <about:blank>
-PASS Origin parsing: <madeupscheme:/example.com/> against <about:blank>
-PASS Origin parsing: <ftps:/example.com/> against <about:blank>
-PASS Origin parsing: <gopher:/example.com/> against <about:blank>
-PASS Origin parsing: <ws:/example.com/> against <about:blank>
-PASS Origin parsing: <wss:/example.com/> against <about:blank>
-PASS Origin parsing: <data:/example.com/> against <about:blank>
-PASS Origin parsing: <javascript:/example.com/> against <about:blank>
-PASS Origin parsing: <mailto:/example.com/> against <about:blank>
-PASS Origin parsing: <http:example.com/> against <about:blank>
-PASS Origin parsing: <ftp:example.com/> against <about:blank>
-PASS Origin parsing: <https:example.com/> against <about:blank>
-PASS Origin parsing: <madeupscheme:example.com/> against <about:blank>
-PASS Origin parsing: <ftps:example.com/> against <about:blank>
-PASS Origin parsing: <gopher:example.com/> against <about:blank>
-PASS Origin parsing: <ws:example.com/> against <about:blank>
-PASS Origin parsing: <wss:example.com/> against <about:blank>
-PASS Origin parsing: <data:example.com/> against <about:blank>
-PASS Origin parsing: <javascript:example.com/> against <about:blank>
-PASS Origin parsing: <mailto:example.com/> against <about:blank>
-PASS Origin parsing: <http:@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/@www.example.com> against <about:blank>
-PASS Origin parsing: <http://@www.example.com> against <about:blank>
-PASS Origin parsing: <http:a:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/a:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http://a:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http://@pple.com> against <about:blank>
-PASS Origin parsing: <http::b@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http://:b@www.example.com> against <about:blank>
-PASS Origin parsing: <http:a:@www.example.com> against <about:blank>
-PASS Origin parsing: <http:/a:@www.example.com> against <about:blank>
-PASS Origin parsing: <http://a:@www.example.com> against <about:blank>
-PASS Origin parsing: <http://www.@pple.com> against <about:blank>
-PASS Origin parsing: <http://:@www.example.com> against <about:blank>
-PASS Origin parsing: </> against <http://www.example.com/test>
-PASS Origin parsing: </test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <.> against <http://www.example.com/test>
-PASS Origin parsing: <..> against <http://www.example.com/test>
-PASS Origin parsing: <test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <./test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <../test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <../aaa/test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <../../test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <中/test.txt> against <http://www.example.com/test>
-PASS Origin parsing: <http://www.example2.com> against <http://www.example.com/test>
-PASS Origin parsing: <//www.example2.com> against <http://www.example.com/test>
-PASS Origin parsing: <http://ExAmPlE.CoM> against <http://other.com/>
-PASS Origin parsing: <http://GOO​⁠goo.com> against <http://other.com/>
-PASS Origin parsing: <\0 http://example.com/ \r > against <about:blank>
-PASS Origin parsing: <http://www.foo。bar.com> against <http://other.com/>
-PASS Origin parsing: <https://x/�?�#�> against <about:blank>
-PASS Origin parsing: <http://Go.com> against <http://other.com/>
-PASS Origin parsing: <http://你好你好> against <http://other.com/>
-FAIL Origin parsing: <https://faß.ExAmPlE/> against <about:blank> assert_equals: origin expected "https://xn--fa-hia.example" but got "https://fass.example"
-PASS Origin parsing: <sc://faß.ExAmPlE/> against <about:blank>
-PASS Origin parsing: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>
-PASS Origin parsing: <http://%30%78%63%30%2e%30%32%35%30.01%2e> against <http://other.com/>
-PASS Origin parsing: <http://0Xc0.0250.01> against <http://other.com/>
-PASS Origin parsing: <http://./> against <about:blank>
-PASS Origin parsing: <http://../> against <about:blank>
-PASS Origin parsing: <http://0..0x300/> against <about:blank>
-PASS Origin parsing: <http://foo:💩@example.com/bar> against <http://other.com/>
-PASS Origin parsing: <#> against <test:test>
-PASS Origin parsing: <#x> against <mailto:x@x.com>
-PASS Origin parsing: <#x> against <data:,>
-PASS Origin parsing: <#x> against <about:blank>
-PASS Origin parsing: <#> against <test:test?test>
-PASS Origin parsing: <https://@test@test@example:800/> against <http://doesnotmatter/>
-PASS Origin parsing: <https://@@@example> against <http://doesnotmatter/>
-PASS Origin parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>
-PASS Origin parsing: <http://host/?'> against <about:blank>
-PASS Origin parsing: <notspecial://host/?'> against <about:blank>
-PASS Origin parsing: </some/path> against <http://user@example.org/smth>
-PASS Origin parsing: <> against <http://user:pass@example.org:21/smth>
-PASS Origin parsing: </some/path> against <http://user:pass@example.org:21/smth>
-PASS Origin parsing: <i> against <sc:/pa/pa>
-PASS Origin parsing: <i> against <sc://ho/pa>
-PASS Origin parsing: <i> against <sc:///pa/pa>
-PASS Origin parsing: <../i> against <sc:/pa/pa>
-PASS Origin parsing: <../i> against <sc://ho/pa>
-PASS Origin parsing: <../i> against <sc:///pa/pa>
-PASS Origin parsing: </i> against <sc:/pa/pa>
-PASS Origin parsing: </i> against <sc://ho/pa>
-PASS Origin parsing: </i> against <sc:///pa/pa>
-PASS Origin parsing: <?i> against <sc:/pa/pa>
-PASS Origin parsing: <?i> against <sc://ho/pa>
-PASS Origin parsing: <?i> against <sc:///pa/pa>
-PASS Origin parsing: <#i> against <sc:sd>
-PASS Origin parsing: <#i> against <sc:sd/sd>
-PASS Origin parsing: <#i> against <sc:/pa/pa>
-PASS Origin parsing: <#i> against <sc://ho/pa>
-PASS Origin parsing: <#i> against <sc:///pa/pa>
-PASS Origin parsing: <about:/../> against <about:blank>
-PASS Origin parsing: <data:/../> against <about:blank>
-PASS Origin parsing: <javascript:/../> against <about:blank>
-PASS Origin parsing: <mailto:/../> against <about:blank>
-PASS Origin parsing: <sc://ñ.test/> against <about:blank>
-FAIL Origin parsing: <x> against <sc://ñ> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <sc:\../> against <about:blank>
-PASS Origin parsing: <sc::a@example.net> against <about:blank>
-PASS Origin parsing: <wow:%NBD> against <about:blank>
-PASS Origin parsing: <wow:%1G> against <about:blank>
-PASS Origin parsing: <wow:￿> against <about:blank>
-FAIL Origin parsing: <http://example.com/U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿?U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿> against <about:blank> Failed to construct 'URL': Invalid URL
-FAIL Origin parsing: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <sc://!"$&'()*+,-.;=_`{}~/> against <about:blank>
-PASS Origin parsing: <ftp://%e2%98%83> against <about:blank>
-PASS Origin parsing: <https://%e2%98%83> against <about:blank>
-PASS Origin parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
-PASS Origin parsing: <http://facebook.com/?foo=%7B%22abc%22> against <about:blank>
-PASS Origin parsing: <https://localhost:3000/jqueryui@1.2.3> against <about:blank>
-PASS Origin parsing: <h	t
-t\rp://h	o
-s\rt:9	0
-0\r0/p	a
-t\rh?q	u
-e\rry#f	r
-a\rg> against <about:blank>
-PASS Origin parsing: <?a=b&c=d> against <http://example.org/foo/bar>
-PASS Origin parsing: <??a=b&c=d> against <http://example.org/foo/bar>
-PASS Origin parsing: <http:> against <http://example.org/foo/bar>
-PASS Origin parsing: <sc:> against <https://example.org/foo/bar>
-PASS Origin parsing: <http://foo.bar/baz?qux#foobar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo"bar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
-PASS Origin parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
-PASS Origin parsing: <http://192.168.257> against <http://other.com/>
-PASS Origin parsing: <http://192.168.257.com> against <http://other.com/>
-PASS Origin parsing: <http://256> against <http://other.com/>
-PASS Origin parsing: <http://256.com> against <http://other.com/>
-PASS Origin parsing: <http://999999999> against <http://other.com/>
-PASS Origin parsing: <http://999999999.com> against <http://other.com/>
-PASS Origin parsing: <http://10000000000.com> against <http://other.com/>
-PASS Origin parsing: <http://4294967295> against <http://other.com/>
-PASS Origin parsing: <http://0xffffffff> against <http://other.com/>
-PASS Origin parsing: <http://256.256.256.256.256> against <http://other.com/>
-PASS Origin parsing: <https://0x.0x.0> against <about:blank>
-PASS Origin parsing: <asdf://%43%7C/> against <about:blank>
-PASS Origin parsing: <http://[1:0::]> against <http://example.net/>
-PASS Origin parsing: <sc://ñ> against <about:blank>
-PASS Origin parsing: <sc://ñ?x> against <about:blank>
-PASS Origin parsing: <sc://ñ#x> against <about:blank>
-FAIL Origin parsing: <#x> against <sc://ñ> Failed to construct 'URL': Invalid URL
-FAIL Origin parsing: <?x> against <sc://ñ> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank>
-PASS Origin parsing: <telnet://user:pass@foobar.com:23/> against <about:blank>
-PASS Origin parsing: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank>
-PASS Origin parsing: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank>
-PASS Origin parsing: <rsync://foo@host:911/sup> against <about:blank>
-PASS Origin parsing: <git://github.com/foo/bar.git> against <about:blank>
-PASS Origin parsing: <irc://myserver.com:6999/channel?passwd> against <about:blank>
-PASS Origin parsing: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank>
-PASS Origin parsing: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank>
-PASS Origin parsing: <git+https://github.com/foo/bar> against <about:blank>
-PASS Origin parsing: <urn:ietf:rfc:2648> against <about:blank>
-PASS Origin parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
-PASS Origin parsing: <non-special:cannot-be-a-base-url-\0~€> against <about:blank>
-PASS Origin parsing: <https://www.example.com/path{path.html?query'=query#fragment<fragment> against <about:blank>
-PASS Origin parsing: <https://user:pass[@foo/bar> against <http://example.org>
-PASS Origin parsing: <foo:// !"$%&'()*+,-.;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <wss:// !"$%&'()*+,-.;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <foo://joe: !"$%&'()*+,-.:;<=>@[\]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <wss://joe: !"$%&'()*+,-.:;<=>@[]^_`{|}~@host/> against <about:blank>
-PASS Origin parsing: <foo://!"$%&'()*+,-.;=_`{}~/> against <about:blank>
-FAIL Origin parsing: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank> Failed to construct 'URL': Invalid URL
-PASS Origin parsing: <foo://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <wss://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <foo://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <wss://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <foo://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-PASS Origin parsing: <wss://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/url/a-element-expected.txt
deleted file mode 100644
index b80c6751..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/url/a-element-expected.txt
+++ /dev/null
@@ -1,648 +0,0 @@
-This is a testharness.js-based test.
-Found 633 tests; 376 PASS, 257 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Loading data…
-PASS Parsing: <http://example	.
-org> against <http://example.org/foo/bar>
-PASS Parsing: <http://user:pass@foo:21/bar;par?b#c> against <http://example.org/foo/bar>
-PASS Parsing: <https://test:@test> against <about:blank>
-PASS Parsing: <https://:@test> against <about:blank>
-FAIL Parsing: <non-special://test:@test/x> against <about:blank> assert_equals: href expected "non-special://test@test/x" but got "non-special://test:@test/x"
-FAIL Parsing: <non-special://:@test/x> against <about:blank> assert_equals: href expected "non-special://test/x" but got "non-special://:@test/x"
-PASS Parsing: <http:foo.com> against <http://example.org/foo/bar>
-PASS Parsing: <	   :foo.com   
-> against <http://example.org/foo/bar>
-PASS Parsing: < foo.com  > against <http://example.org/foo/bar>
-PASS Parsing: <a:	 foo.com> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
-PASS Parsing: <lolscheme:x x#x x> against <about:blank>
-PASS Parsing: <http://f:/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:0/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:b/c> against <http://example.org/foo/bar>
-FAIL Parsing: <http://f: /c> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://f: /c" but got "http://f:%20/c"
-PASS Parsing: <http://f:
-/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:fifty-two/c> against <http://example.org/foo/bar>
-PASS Parsing: <http://f:999999/c> against <http://example.org/foo/bar>
-FAIL Parsing: <non-special://f:999999/c> against <http://example.org/foo/bar> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://f: 21 / b ? d # e > against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://f: 21 / b ? d # e " but got "http://f:%2021%20/%20b%20?%20d%20#%20e"
-PASS Parsing: <> against <http://example.org/foo/bar>
-PASS Parsing: <  	> against <http://example.org/foo/bar>
-PASS Parsing: <:foo.com/> against <http://example.org/foo/bar>
-PASS Parsing: <:foo.com\> against <http://example.org/foo/bar>
-PASS Parsing: <:> against <http://example.org/foo/bar>
-PASS Parsing: <:a> against <http://example.org/foo/bar>
-PASS Parsing: <:/> against <http://example.org/foo/bar>
-PASS Parsing: <:\> against <http://example.org/foo/bar>
-PASS Parsing: <:#> against <http://example.org/foo/bar>
-PASS Parsing: <#> against <http://example.org/foo/bar>
-PASS Parsing: <#/> against <http://example.org/foo/bar>
-PASS Parsing: <#\> against <http://example.org/foo/bar>
-PASS Parsing: <#;?> against <http://example.org/foo/bar>
-PASS Parsing: <?> against <http://example.org/foo/bar>
-PASS Parsing: </> against <http://example.org/foo/bar>
-PASS Parsing: <:23> against <http://example.org/foo/bar>
-PASS Parsing: </:23> against <http://example.org/foo/bar>
-PASS Parsing: <\x> against <http://example.org/foo/bar>
-PASS Parsing: <\\x\hello> against <http://example.org/foo/bar>
-PASS Parsing: <::> against <http://example.org/foo/bar>
-PASS Parsing: <::23> against <http://example.org/foo/bar>
-FAIL Parsing: <foo://> against <http://example.org/foo/bar> assert_equals: pathname expected "" but got "//"
-PASS Parsing: <http://a:b@c:29/d> against <http://example.org/foo/bar>
-PASS Parsing: <http::@c:29> against <http://example.org/foo/bar>
-PASS Parsing: <http://&a:foo(b]c@d:2/> against <http://example.org/foo/bar>
-PASS Parsing: <http://::@c@d:2> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo.com/\@> against <http://example.org/foo/bar>
-PASS Parsing: <http:\\foo.com\> against <http://example.org/foo/bar>
-PASS Parsing: <http:\\a\b:c\d@foo.com\> against <http://example.org/foo/bar>
-PASS Parsing: <foo:/> against <http://example.org/foo/bar>
-PASS Parsing: <foo:/bar.com/> against <http://example.org/foo/bar>
-FAIL Parsing: <foo://///////> against <http://example.org/foo/bar> assert_equals: pathname expected "///////" but got "/////////"
-FAIL Parsing: <foo://///////bar.com/> against <http://example.org/foo/bar> assert_equals: pathname expected "///////bar.com/" but got "/////////bar.com/"
-FAIL Parsing: <foo:////://///> against <http://example.org/foo/bar> assert_equals: pathname expected "//://///" but got "////://///"
-PASS Parsing: <c:/foo> against <http://example.org/foo/bar>
-PASS Parsing: <//foo/bar> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo/path;a??e#f#g> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo/abcd?efgh?ijkl> against <http://example.org/foo/bar>
-PASS Parsing: <http://foo/abcd#foo?bar> against <http://example.org/foo/bar>
-PASS Parsing: <[61:24:74]:98> against <http://example.org/foo/bar>
-PASS Parsing: <http:[61:27]/:foo> against <http://example.org/foo/bar>
-FAIL Parsing: <http://[1::2]:3:4> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://[1::2]:3:4" but got "http://[1::2]:3:4/"
-FAIL Parsing: <http://2001::1> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://2001::1" but got "http://2001::1/"
-FAIL Parsing: <http://2001::1]> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://2001::1]" but got "http://2001::1]/"
-FAIL Parsing: <http://2001::1]:80> against <http://example.org/foo/bar> assert_equals: failure should set href to input expected "http://2001::1]:80" but got "http://2001::1]/"
-PASS Parsing: <http://[2001::1]> against <http://example.org/foo/bar>
-PASS Parsing: <http://[::127.0.0.1]> against <http://example.org/foo/bar>
-PASS Parsing: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>
-PASS Parsing: <http://[2001::1]:80> against <http://example.org/foo/bar>
-PASS Parsing: <http:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ftp:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <https:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <madeupscheme:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <file:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <file://example:1/> against <about:blank>
-PASS Parsing: <file://example:test/> against <about:blank>
-FAIL Parsing: <file://example%/> against <about:blank> assert_equals: failure should set href to input expected "file://example%/" but got "file://example%25/"
-PASS Parsing: <file://[example]/> against <about:blank>
-PASS Parsing: <ftps:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <gopher:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ws:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <wss:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <data:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <javascript:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <mailto:/example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <http:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ftp:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <https:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <madeupscheme:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ftps:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <gopher:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <ws:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <wss:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <data:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <javascript:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: <mailto:example.com/> against <http://example.org/foo/bar>
-PASS Parsing: </a/b/c> against <http://example.org/foo/bar>
-PASS Parsing: </a/ /c> against <http://example.org/foo/bar>
-PASS Parsing: </a%2fc> against <http://example.org/foo/bar>
-PASS Parsing: </a/%2f/c> against <http://example.org/foo/bar>
-PASS Parsing: <#β> against <http://example.org/foo/bar>
-PASS Parsing: <data:text/html,test#test> against <http://example.org/foo/bar>
-PASS Parsing: <tel:1234567890> against <http://example.org/foo/bar>
-FAIL Parsing: <ssh://example.com/foo/bar.git> against <http://example.org/> assert_equals: host expected "example.com" but got ""
-FAIL Parsing: <file:c:\foo\bar.html> against <file:///tmp/mock/path> assert_equals: href expected "file:///c:/foo/bar.html" but got "file:///tmp/mock/c:/foo/bar.html"
-FAIL Parsing: <  File:c|////foo\bar.html> against <file:///tmp/mock/path> assert_equals: href expected "file:///c:////foo/bar.html" but got "file:///tmp/mock/c%7C////foo/bar.html"
-FAIL Parsing: <C|/foo/bar> against <file:///tmp/mock/path> assert_equals: href expected "file:///C:/foo/bar" but got "file:///tmp/mock/C%7C/foo/bar"
-FAIL Parsing: </C|\foo\bar> against <file:///tmp/mock/path> assert_equals: href expected "file:///C:/foo/bar" but got "file:///C%7C/foo/bar"
-FAIL Parsing: <//C|/foo/bar> against <file:///tmp/mock/path> assert_equals: href expected "file:///C:/foo/bar" but got "file://c%7C/foo/bar"
-PASS Parsing: <//server/file> against <file:///tmp/mock/path>
-PASS Parsing: <\\server\file> against <file:///tmp/mock/path>
-PASS Parsing: </\server/file> against <file:///tmp/mock/path>
-PASS Parsing: <file:///foo/bar.txt> against <file:///tmp/mock/path>
-PASS Parsing: <file:///home/me> against <file:///tmp/mock/path>
-PASS Parsing: <//> against <file:///tmp/mock/path>
-PASS Parsing: <///> against <file:///tmp/mock/path>
-PASS Parsing: <///test> against <file:///tmp/mock/path>
-PASS Parsing: <file://test> against <file:///tmp/mock/path>
-FAIL Parsing: <file://localhost> against <file:///tmp/mock/path> assert_equals: href expected "file:///" but got "file://localhost/"
-FAIL Parsing: <file://localhost/> against <file:///tmp/mock/path> assert_equals: href expected "file:///" but got "file://localhost/"
-FAIL Parsing: <file://localhost/test> against <file:///tmp/mock/path> assert_equals: href expected "file:///test" but got "file://localhost/test"
-PASS Parsing: <test> against <file:///tmp/mock/path>
-PASS Parsing: <file:test> against <file:///tmp/mock/path>
-PASS Parsing: <http://example.com/././foo> against <about:blank>
-PASS Parsing: <http://example.com/./.foo> against <about:blank>
-PASS Parsing: <http://example.com/foo/.> against <about:blank>
-PASS Parsing: <http://example.com/foo/./> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/..> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/../> against <about:blank>
-PASS Parsing: <http://example.com/foo/..bar> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/../ton> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar/../ton/../../a> against <about:blank>
-PASS Parsing: <http://example.com/foo/../../..> against <about:blank>
-PASS Parsing: <http://example.com/foo/../../../ton> against <about:blank>
-PASS Parsing: <http://example.com/foo/%2e> against <about:blank>
-FAIL Parsing: <http://example.com/foo/%2e%2> against <about:blank> assert_equals: href expected "http://example.com/foo/%2e%2" but got "http://example.com/foo/.%2"
-FAIL Parsing: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank> assert_equals: href expected "http://example.com/%2e.bar" but got "http://example.com/..bar"
-PASS Parsing: <http://example.com////../..> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar//../..> against <about:blank>
-PASS Parsing: <http://example.com/foo/bar//..> against <about:blank>
-PASS Parsing: <http://example.com/foo> against <about:blank>
-PASS Parsing: <http://example.com/%20foo> against <about:blank>
-PASS Parsing: <http://example.com/foo%> against <about:blank>
-PASS Parsing: <http://example.com/foo%2> against <about:blank>
-PASS Parsing: <http://example.com/foo%2zbar> against <about:blank>
-PASS Parsing: <http://example.com/foo%2©zbar> against <about:blank>
-FAIL Parsing: <http://example.com/foo%41%7a> against <about:blank> assert_equals: href expected "http://example.com/foo%41%7a" but got "http://example.com/fooAz"
-PASS Parsing: <http://example.com/foo	‘%91> against <about:blank>
-FAIL Parsing: <http://example.com/foo%00%51> against <about:blank> assert_equals: href expected "http://example.com/foo%00%51" but got "http://example.com/foo%00Q"
-PASS Parsing: <http://example.com/(%28:%3A%29)> against <about:blank>
-PASS Parsing: <http://example.com/%3A%3a%3C%3c> against <about:blank>
-PASS Parsing: <http://example.com/foo	bar> against <about:blank>
-PASS Parsing: <http://example.com\\foo\\bar> against <about:blank>
-PASS Parsing: <http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd> against <about:blank>
-PASS Parsing: <http://example.com/@asdf%40> against <about:blank>
-PASS Parsing: <http://example.com/你好你好> against <about:blank>
-PASS Parsing: <http://example.com/‥/foo> against <about:blank>
-PASS Parsing: <http://example.com//foo> against <about:blank>
-PASS Parsing: <http://example.com/‮/foo/‭/bar> against <about:blank>
-PASS Parsing: <http://www.google.com/foo?bar=baz#> against <about:blank>
-PASS Parsing: <http://www.google.com/foo?bar=baz# »> against <about:blank>
-PASS Parsing: <data:test# »> against <about:blank>
-PASS Parsing: <http://www.google.com> against <about:blank>
-PASS Parsing: <http://192.0x00A80001> against <about:blank>
-FAIL Parsing: <http://www/foo%2Ehtml> against <about:blank> assert_equals: href expected "http://www/foo%2Ehtml" but got "http://www/foo.html"
-PASS Parsing: <http://www/foo/%2E/html> against <about:blank>
-PASS Parsing: <http://user:pass@/> against <about:blank>
-PASS Parsing: <http://%25DOMAIN:foobar@foodomain.com/> against <about:blank>
-PASS Parsing: <http:\\www.google.com\foo> against <about:blank>
-PASS Parsing: <http://foo:80/> against <about:blank>
-PASS Parsing: <http://foo:81/> against <about:blank>
-FAIL Parsing: <httpa://foo:80/> against <about:blank> assert_equals: host expected "foo:80" but got ""
-PASS Parsing: <http://foo:-80/> against <about:blank>
-PASS Parsing: <https://foo:443/> against <about:blank>
-PASS Parsing: <https://foo:80/> against <about:blank>
-PASS Parsing: <ftp://foo:21/> against <about:blank>
-PASS Parsing: <ftp://foo:80/> against <about:blank>
-FAIL Parsing: <gopher://foo:70/> against <about:blank> assert_equals: host expected "foo:70" but got ""
-FAIL Parsing: <gopher://foo:443/> against <about:blank> assert_equals: host expected "foo:443" but got ""
-PASS Parsing: <ws://foo:80/> against <about:blank>
-PASS Parsing: <ws://foo:81/> against <about:blank>
-PASS Parsing: <ws://foo:443/> against <about:blank>
-PASS Parsing: <ws://foo:815/> against <about:blank>
-PASS Parsing: <wss://foo:80/> against <about:blank>
-PASS Parsing: <wss://foo:81/> against <about:blank>
-PASS Parsing: <wss://foo:443/> against <about:blank>
-PASS Parsing: <wss://foo:815/> against <about:blank>
-PASS Parsing: <http:/example.com/> against <about:blank>
-PASS Parsing: <ftp:/example.com/> against <about:blank>
-PASS Parsing: <https:/example.com/> against <about:blank>
-PASS Parsing: <madeupscheme:/example.com/> against <about:blank>
-PASS Parsing: <file:/example.com/> against <about:blank>
-PASS Parsing: <ftps:/example.com/> against <about:blank>
-PASS Parsing: <gopher:/example.com/> against <about:blank>
-PASS Parsing: <ws:/example.com/> against <about:blank>
-PASS Parsing: <wss:/example.com/> against <about:blank>
-PASS Parsing: <data:/example.com/> against <about:blank>
-PASS Parsing: <javascript:/example.com/> against <about:blank>
-PASS Parsing: <mailto:/example.com/> against <about:blank>
-PASS Parsing: <http:example.com/> against <about:blank>
-PASS Parsing: <ftp:example.com/> against <about:blank>
-PASS Parsing: <https:example.com/> against <about:blank>
-PASS Parsing: <madeupscheme:example.com/> against <about:blank>
-PASS Parsing: <ftps:example.com/> against <about:blank>
-PASS Parsing: <gopher:example.com/> against <about:blank>
-PASS Parsing: <ws:example.com/> against <about:blank>
-PASS Parsing: <wss:example.com/> against <about:blank>
-PASS Parsing: <data:example.com/> against <about:blank>
-PASS Parsing: <javascript:example.com/> against <about:blank>
-PASS Parsing: <mailto:example.com/> against <about:blank>
-PASS Parsing: <http:@www.example.com> against <about:blank>
-PASS Parsing: <http:/@www.example.com> against <about:blank>
-PASS Parsing: <http://@www.example.com> against <about:blank>
-PASS Parsing: <http:a:b@www.example.com> against <about:blank>
-PASS Parsing: <http:/a:b@www.example.com> against <about:blank>
-PASS Parsing: <http://a:b@www.example.com> against <about:blank>
-PASS Parsing: <http://@pple.com> against <about:blank>
-PASS Parsing: <http::b@www.example.com> against <about:blank>
-PASS Parsing: <http:/:b@www.example.com> against <about:blank>
-PASS Parsing: <http://:b@www.example.com> against <about:blank>
-FAIL Parsing: <http:/:@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/:@/www.example.com" but got "http:///www.example.com"
-PASS Parsing: <http://user@/www.example.com> against <about:blank>
-FAIL Parsing: <http:@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:@/www.example.com" but got "http:///www.example.com"
-FAIL Parsing: <http:/@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/@/www.example.com" but got "http:///www.example.com"
-FAIL Parsing: <http://@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http://@/www.example.com" but got "http:///www.example.com"
-FAIL Parsing: <https:@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "https:@/www.example.com" but got "https:///www.example.com"
-FAIL Parsing: <http:a:b@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:a:b@/www.example.com" but got "http://a:b@/www.example.com"
-FAIL Parsing: <http:/a:b@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/a:b@/www.example.com" but got "http://a:b@/www.example.com"
-PASS Parsing: <http://a:b@/www.example.com> against <about:blank>
-FAIL Parsing: <http::@/www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http::@/www.example.com" but got "http:///www.example.com"
-PASS Parsing: <http:a:@www.example.com> against <about:blank>
-PASS Parsing: <http:/a:@www.example.com> against <about:blank>
-PASS Parsing: <http://a:@www.example.com> against <about:blank>
-PASS Parsing: <http://www.@pple.com> against <about:blank>
-FAIL Parsing: <http:@:www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:@:www.example.com" but got "http://:www.example.com/"
-FAIL Parsing: <http:/@:www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http:/@:www.example.com" but got "http://:www.example.com/"
-FAIL Parsing: <http://@:www.example.com> against <about:blank> assert_equals: failure should set href to input expected "http://@:www.example.com" but got "http://:www.example.com/"
-PASS Parsing: <http://:@www.example.com> against <about:blank>
-PASS Parsing: </> against <http://www.example.com/test>
-PASS Parsing: </test.txt> against <http://www.example.com/test>
-PASS Parsing: <.> against <http://www.example.com/test>
-PASS Parsing: <..> against <http://www.example.com/test>
-PASS Parsing: <test.txt> against <http://www.example.com/test>
-PASS Parsing: <./test.txt> against <http://www.example.com/test>
-PASS Parsing: <../test.txt> against <http://www.example.com/test>
-PASS Parsing: <../aaa/test.txt> against <http://www.example.com/test>
-PASS Parsing: <../../test.txt> against <http://www.example.com/test>
-PASS Parsing: <中/test.txt> against <http://www.example.com/test>
-PASS Parsing: <http://www.example2.com> against <http://www.example.com/test>
-PASS Parsing: <//www.example2.com> against <http://www.example.com/test>
-PASS Parsing: <file:...> against <http://www.example.com/test>
-PASS Parsing: <file:..> against <http://www.example.com/test>
-PASS Parsing: <file:a> against <http://www.example.com/test>
-PASS Parsing: <http://ExAmPlE.CoM> against <http://other.com/>
-FAIL Parsing: <http://example example.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://Goo%20 goo%7C|.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://[]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[]" but got "http://[]/"
-FAIL Parsing: <http://[:]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[:]" but got "http://[:]/"
-FAIL Parsing: <http://GOO  goo.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://GOO​⁠goo.com> against <http://other.com/>
-PASS Parsing: <\0 http://example.com/ \r > against <about:blank>
-PASS Parsing: <http://www.foo。bar.com> against <http://other.com/>
-FAIL Parsing: <http://﷐zyx.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://﷐zyx.com" but got "http://%EF%BF%BDzyx.com/"
-FAIL Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%ef%b7%90zyx.com" but got "http://%EF%BF%BDzyx.com/"
-FAIL Parsing: <https://�> against <about:blank> assert_equals: failure should set href to input expected "https://\ufffd" but got "https://%EF%BF%BD/"
-FAIL Parsing: <https://%EF%BF%BD> against <about:blank> assert_equals: failure should set href to input expected "https://%EF%BF%BD" but got "https://%EF%BF%BD/"
-PASS Parsing: <https://x/�?�#�> against <about:blank>
-FAIL Parsing: <http://a.b.c.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://Go.com> against <http://other.com/>
-FAIL Parsing: <http://%41.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://%ef%bc%85%ef%bc%94%ef%bc%91.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://%00.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%00.com" but got "http://%00.com/"
-FAIL Parsing: <http://%ef%bc%85%ef%bc%90%ef%bc%90.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%ef%bc%85%ef%bc%90%ef%bc%90.com" but got "http://%00.com/"
-PASS Parsing: <http://你好你好> against <http://other.com/>
-FAIL Parsing: <https://faß.ExAmPlE/> against <about:blank> assert_equals: href expected "https://xn--fa-hia.example/" but got "https://fass.example/"
-FAIL Parsing: <sc://faß.ExAmPlE/> against <about:blank> assert_equals: host expected "fa%C3%9F.ExAmPlE" but got ""
-FAIL Parsing: <http://%zz%66%a.com> against <http://other.com/> assert_equals: failure should set href to input expected "http://%zz%66%a.com" but got "http://%25zzf%25a.com/"
-FAIL Parsing: <http://%25> against <http://other.com/> assert_equals: failure should set href to input expected "http://%25" but got "http://%25/"
-FAIL Parsing: <http://hello%00> against <http://other.com/> assert_equals: failure should set href to input expected "http://hello%00" but got "http://hello%00/"
-PASS Parsing: <http://%30%78%63%30%2e%30%32%35%30.01> against <http://other.com/>
-PASS Parsing: <http://%30%78%63%30%2e%30%32%35%30.01%2e> against <http://other.com/>
-FAIL Parsing: <http://192.168.0.257> against <http://other.com/> assert_equals: failure should set href to input expected "http://192.168.0.257" but got "http://192.168.0.257/"
-FAIL Parsing: <http://%3g%78%63%30%2e%30%32%35%30%2E.01> against <http://other.com/> assert_equals: failure should set href to input expected "http://%3g%78%63%30%2e%30%32%35%30%2E.01" but got "http://%253gxc0.0250..01/"
-FAIL Parsing: <http://192.168.0.1 hello> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <https://x x:12> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
-PASS Parsing: <http://./> against <about:blank>
-PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
-PASS Parsing: <http://[www.google.com]/> against <about:blank>
-FAIL Parsing: <http://[google.com]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[google.com]" but got "http://[google.com]/"
-FAIL Parsing: <http://[::1.2.3.4x]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[::1.2.3.4x]" but got "http://[::1.2.3.4x]/"
-FAIL Parsing: <http://[::1.2.3.]> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://[::1.2.]> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://[::1.]> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://foo:💩@example.com/bar> against <http://other.com/>
-PASS Parsing: <#> against <test:test>
-PASS Parsing: <#x> against <mailto:x@x.com>
-FAIL Parsing: <#x> against <data:,> assert_equals: href expected "data:,#x" but got "mailto:x@x.com#x"
-PASS Parsing: <#x> against <about:blank>
-PASS Parsing: <#> against <test:test?test>
-PASS Parsing: <https://@test@test@example:800/> against <http://doesnotmatter/>
-PASS Parsing: <https://@@@example> against <http://doesnotmatter/>
-PASS Parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>
-PASS Parsing: <http://host/?'> against <about:blank>
-FAIL Parsing: <notspecial://host/?'> against <about:blank> assert_equals: host expected "host" but got ""
-PASS Parsing: </some/path> against <http://user@example.org/smth>
-PASS Parsing: <> against <http://user:pass@example.org:21/smth>
-PASS Parsing: </some/path> against <http://user:pass@example.org:21/smth>
-FAIL Parsing: <i> against <sc:sd> assert_equals: failure should set href to input expected "i" but got ""
-FAIL Parsing: <i> against <sc:sd/sd> assert_equals: failure should set href to input expected "i" but got ""
-PASS Parsing: <i> against <sc:/pa/pa>
-FAIL Parsing: <i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <i> against <sc:///pa/pa> assert_equals: pathname expected "/pa/i" but got "///pa/i"
-FAIL Parsing: <../i> against <sc:sd> assert_equals: failure should set href to input expected "../i" but got ""
-FAIL Parsing: <../i> against <sc:sd/sd> assert_equals: failure should set href to input expected "../i" but got ""
-PASS Parsing: <../i> against <sc:/pa/pa>
-FAIL Parsing: <../i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <../i> against <sc:///pa/pa> assert_equals: href expected "sc:///i" but got "sc:///pa/i"
-FAIL Parsing: </i> against <sc:sd> assert_equals: failure should set href to input expected "/i" but got ""
-FAIL Parsing: </i> against <sc:sd/sd> assert_equals: failure should set href to input expected "/i" but got ""
-PASS Parsing: </i> against <sc:/pa/pa>
-FAIL Parsing: </i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: </i> against <sc:///pa/pa> assert_equals: href expected "sc:///i" but got "sc:///pa/i"
-FAIL Parsing: <?i> against <sc:sd> assert_equals: failure should set href to input expected "?i" but got ""
-FAIL Parsing: <?i> against <sc:sd/sd> assert_equals: failure should set href to input expected "?i" but got ""
-PASS Parsing: <?i> against <sc:/pa/pa>
-FAIL Parsing: <?i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <?i> against <sc:///pa/pa> assert_equals: pathname expected "/pa/pa" but got "///pa/pa"
-PASS Parsing: <#i> against <sc:sd>
-PASS Parsing: <#i> against <sc:sd/sd>
-PASS Parsing: <#i> against <sc:/pa/pa>
-FAIL Parsing: <#i> against <sc://ho/pa> assert_equals: host expected "ho" but got ""
-FAIL Parsing: <#i> against <sc:///pa/pa> assert_equals: pathname expected "/pa/pa" but got "///pa/pa"
-FAIL Parsing: <about:/../> against <about:blank> assert_equals: href expected "about:/" but got "about:/../"
-FAIL Parsing: <data:/../> against <about:blank> assert_equals: href expected "data:/" but got "data:/../"
-FAIL Parsing: <javascript:/../> against <about:blank> assert_equals: href expected "javascript:/" but got "javascript:/../"
-FAIL Parsing: <mailto:/../> against <about:blank> assert_equals: href expected "mailto:/" but got "mailto:/../"
-FAIL Parsing: <sc://ñ.test/> against <about:blank> assert_equals: host expected "%C3%B1.test" but got ""
-FAIL Parsing: <sc://\0/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc:// /> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://%/> against <about:blank> assert_equals: host expected "%" but got ""
-FAIL Parsing: <sc://@/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://te@s:t@/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://:/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://:12/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://[/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://\/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <sc://]/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <x> against <sc://ñ> assert_equals: href expected "sc://%C3%B1/x" but got "sc://%C3%B1"
-PASS Parsing: <sc:\../> against <about:blank>
-PASS Parsing: <sc::a@example.net> against <about:blank>
-PASS Parsing: <wow:%NBD> against <about:blank>
-PASS Parsing: <wow:%1G> against <about:blank>
-FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
-FAIL Parsing: <http://example.com/U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿?U+d800𐟾U+dfff﷐﷏﷯ﷰ￾￿> against <about:blank> assert_equals: href expected "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF" but got "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%BF%BD%EF%B7%8F%EF%BF%BD%EF%B7%B0%EF%BF%BD%EF%BF%BD?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%BF%BD%EF%B7%8F%EF%BF%BD%EF%B7%B0%EF%BF%BD%EF%BF%BD"
-FAIL Parsing: <http://a<b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://a>b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://a^b> against <about:blank> assert_equals: failure should set href to input expected "http://a^b" but got "http://a%5Eb/"
-FAIL Parsing: <non-special://a<b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <non-special://a>b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <non-special://a^b> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <foo://ho\0st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <foo://ho|st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <foo://ho	st/> against <about:blank> assert_equals: host expected "host" but got ""
-FAIL Parsing: <foo://ho
-st/> against <about:blank> assert_equals: host expected "host" but got ""
-FAIL Parsing: <foo://ho\rst/> against <about:blank> assert_equals: host expected "host" but got ""
-PASS Parsing: <http://ho%00st/> against <about:blank>
-PASS Parsing: <http://ho%09st/> against <about:blank>
-PASS Parsing: <http://ho%0Ast/> against <about:blank>
-PASS Parsing: <http://ho%0Dst/> against <about:blank>
-FAIL Parsing: <http://ho%20st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://ho%23st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://ho%2Fst/> against <about:blank>
-FAIL Parsing: <http://ho%3Ast/> against <about:blank> assert_equals: failure should set href to input expected "http://ho%3Ast/" but got "http://ho:st/"
-FAIL Parsing: <http://ho%3Cst/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://ho%3Est/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <http://ho%3Fst/> against <about:blank>
-FAIL Parsing: <http://ho%40st/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://ho%5Bst/> against <about:blank> assert_equals: failure should set href to input expected "http://ho%5Bst/" but got "http://ho[st/"
-PASS Parsing: <http://ho%5Cst/> against <about:blank>
-FAIL Parsing: <http://ho%5Dst/> against <about:blank> assert_equals: failure should set href to input expected "http://ho%5Dst/" but got "http://ho]st/"
-FAIL Parsing: <http://ho%7Cst/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <http://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: href expected "http://\x1f!\"$&'()*+,-.;=_`{}~/" but got "http://%1F%21%22%24%26%27%28%29%2A+%2C-.%3B%3D_%60%7B%7D%7E/"
-FAIL Parsing: <sc://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: host expected "%1F!\"$&'()*+,-.;=_`{}~" but got ""
-FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
-FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
-FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
-FAIL Parsing: <https://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%A0/" but got "https://example.com%EF%BF%BD/"
-PASS Parsing: <ftp://%e2%98%83> against <about:blank>
-PASS Parsing: <https://%e2%98%83> against <about:blank>
-PASS Parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
-PASS Parsing: <http://facebook.com/?foo=%7B%22abc%22> against <about:blank>
-PASS Parsing: <https://localhost:3000/jqueryui@1.2.3> against <about:blank>
-PASS Parsing: <h	t
-t\rp://h	o
-s\rt:9	0
-0\r0/p	a
-t\rh?q	u
-e\rry#f	r
-a\rg> against <about:blank>
-PASS Parsing: <?a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing: <??a=b&c=d> against <http://example.org/foo/bar>
-PASS Parsing: <http:> against <http://example.org/foo/bar>
-PASS Parsing: <http:> against <https://example.org/foo/bar>
-PASS Parsing: <sc:> against <https://example.org/foo/bar>
-PASS Parsing: <http://foo.bar/baz?qux#foobar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo"bar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
-PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
-PASS Parsing: <http://192.168.257> against <http://other.com/>
-PASS Parsing: <http://192.168.257.com> against <http://other.com/>
-PASS Parsing: <http://256> against <http://other.com/>
-PASS Parsing: <http://256.com> against <http://other.com/>
-PASS Parsing: <http://999999999> against <http://other.com/>
-PASS Parsing: <http://999999999.com> against <http://other.com/>
-FAIL Parsing: <http://10000000000> against <http://other.com/> assert_equals: failure should set href to input expected "http://10000000000" but got "http://10000000000/"
-PASS Parsing: <http://10000000000.com> against <http://other.com/>
-PASS Parsing: <http://4294967295> against <http://other.com/>
-FAIL Parsing: <http://4294967296> against <http://other.com/> assert_equals: failure should set href to input expected "http://4294967296" but got "http://4294967296/"
-PASS Parsing: <http://0xffffffff> against <http://other.com/>
-FAIL Parsing: <http://0xffffffff1> against <http://other.com/> assert_equals: failure should set href to input expected "http://0xffffffff1" but got "http://0xffffffff1/"
-FAIL Parsing: <http://256.256.256.256> against <http://other.com/> assert_equals: failure should set href to input expected "http://256.256.256.256" but got "http://256.256.256.256/"
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
-PASS Parsing: <https://0x.0x.0> against <about:blank>
-PASS Parsing: <https://0x100000000/test> against <about:blank>
-PASS Parsing: <https://256.0.0.1/test> against <about:blank>
-PASS Parsing: <file:///C%3A/> against <about:blank>
-PASS Parsing: <file:///C%7C/> against <about:blank>
-FAIL Parsing: <file://%43%3A> against <about:blank> assert_equals: failure should set href to input expected "file://%43%3A" but got "file://c:/"
-FAIL Parsing: <file://%43%7C> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <file://%43|> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <file://C%7C> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <file://%43%7C/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <https://%43%7C/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <asdf://%43|/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-FAIL Parsing: <asdf://%43%7C/> against <about:blank> assert_equals: host expected "%43%7C" but got ""
-PASS Parsing: <pix/submit.gif> against <file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html>
-FAIL Parsing: <..> against <file:///C:/> assert_equals: href expected "file:///C:/" but got "file:///"
-PASS Parsing: <..> against <file:///>
-FAIL Parsing: </> against <file:///C:/a/b> assert_equals: href expected "file:///C:/" but got "file:///"
-FAIL Parsing: </> against <file://h/C:/a/b> assert_equals: href expected "file://h/C:/" but got "file:///"
-FAIL Parsing: </> against <file://h/a/b> assert_equals: href expected "file://h/" but got "file:///"
-FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file://d:/"
-FAIL Parsing: <//d:/..> against <file:///C:/a/b> assert_equals: href expected "file:///d:/" but got "file://d:/"
-PASS Parsing: <..> against <file:///ab:/>
-PASS Parsing: <..> against <file:///1:/>
-PASS Parsing: <> against <file:///test?test#test>
-PASS Parsing: <file:> against <file:///test?test#test>
-PASS Parsing: <?x> against <file:///test?test#test>
-PASS Parsing: <file:?x> against <file:///test?test#test>
-PASS Parsing: <#x> against <file:///test?test#test>
-PASS Parsing: <file:#x> against <file:///test?test#test>
-FAIL Parsing: <file:\\//> against <about:blank> assert_equals: href expected "file:////" but got "file:///"
-FAIL Parsing: <file:\\\\> against <about:blank> assert_equals: href expected "file:////" but got "file:///"
-FAIL Parsing: <file:\\\\?fox> against <about:blank> assert_equals: href expected "file:////?fox" but got "file:///?fox"
-FAIL Parsing: <file:\\\\#guppy> against <about:blank> assert_equals: href expected "file:////#guppy" but got "file:///#guppy"
-PASS Parsing: <file://spider///> against <about:blank>
-FAIL Parsing: <file:\\localhost//> against <about:blank> assert_equals: href expected "file:////" but got "file://localhost//"
-PASS Parsing: <file:///localhost//cat> against <about:blank>
-FAIL Parsing: <file://\/localhost//cat> against <about:blank> assert_equals: href expected "file:////localhost//cat" but got "file:///localhost//cat"
-FAIL Parsing: <file://localhost//a//../..//> against <about:blank> assert_equals: href expected "file://///" but got "file://localhost///"
-FAIL Parsing: </////mouse> against <file:///elephant> assert_equals: href expected "file://///mouse" but got "file:///mouse"
-PASS Parsing: <\//pig> against <file://lion/>
-FAIL Parsing: <\/localhost//pig> against <file://lion/> assert_equals: href expected "file:////pig" but got "file://localhost//pig"
-FAIL Parsing: <//localhost//pig> against <file://lion/> assert_equals: href expected "file:////pig" but got "file://localhost//pig"
-PASS Parsing: </..//localhost//pig> against <file://lion/>
-PASS Parsing: <file://> against <file://ape/>
-PASS Parsing: </rooibos> against <file://tea/>
-PASS Parsing: </?chai> against <file://tea/>
-FAIL Parsing: <C|> against <file://host/dir/file> assert_equals: href expected "file://host/C:" but got "file://host/dir/C%7C"
-FAIL Parsing: <C|> against <file://host/D:/dir1/dir2/file> assert_equals: href expected "file://host/C:" but got "file://host/D:/dir1/dir2/C%7C"
-FAIL Parsing: <C|#> against <file://host/dir/file> assert_equals: href expected "file://host/C:#" but got "file://host/dir/C%7C#"
-FAIL Parsing: <C|?> against <file://host/dir/file> assert_equals: href expected "file://host/C:?" but got "file://host/dir/C%7C?"
-FAIL Parsing: <C|/> against <file://host/dir/file> assert_equals: href expected "file://host/C:/" but got "file://host/dir/C%7C/"
-FAIL Parsing: <C|
-/> against <file://host/dir/file> assert_equals: href expected "file://host/C:/" but got "file://host/dir/C%7C/"
-FAIL Parsing: <C|\> against <file://host/dir/file> assert_equals: href expected "file://host/C:/" but got "file://host/dir/C%7C/"
-PASS Parsing: <C> against <file://host/dir/file>
-FAIL Parsing: <C|a> against <file://host/dir/file> assert_equals: href expected "file://host/dir/C|a" but got "file://host/dir/C%7Ca"
-PASS Parsing: </c:/foo/bar> against <file:///c:/baz/qux>
-FAIL Parsing: </c|/foo/bar> against <file:///c:/baz/qux> assert_equals: href expected "file:///c:/foo/bar" but got "file:///c%7C/foo/bar"
-PASS Parsing: <file:\c:\foo\bar> against <file:///c:/baz/qux>
-PASS Parsing: </c:/foo/bar> against <file://host/path>
-PASS Parsing: <file://example.net/C:/> against <about:blank>
-PASS Parsing: <file://1.2.3.4/C:/> against <about:blank>
-PASS Parsing: <file://[1::8]/C:/> against <about:blank>
-FAIL Parsing: <C|/> against <file://host/> assert_equals: href expected "file://host/C:/" but got "file://host/C%7C/"
-PASS Parsing: </C:/> against <file://host/>
-PASS Parsing: <file:C:/> against <file://host/>
-PASS Parsing: <file:/C:/> against <file://host/>
-FAIL Parsing: <//C:/> against <file://host/> assert_equals: href expected "file:///C:/" but got "file://c:/"
-FAIL Parsing: <file://C:/> against <file://host/> assert_equals: href expected "file:///C:/" but got "file://c:/"
-PASS Parsing: <///C:/> against <file://host/>
-PASS Parsing: <file:///C:/> against <file://host/>
-FAIL Parsing: <file:/C|/> against <about:blank> assert_equals: href expected "file:///C:/" but got "file:///C%7C/"
-FAIL Parsing: <file://C|/> against <about:blank> assert_equals: href expected "file:///C:/" but got "file://c%7C/"
-PASS Parsing: <file:> against <about:blank>
-PASS Parsing: <file:?q=v> against <about:blank>
-PASS Parsing: <file:#frag> against <about:blank>
-PASS Parsing: <file:///Y:> against <about:blank>
-PASS Parsing: <file:///Y:/> against <about:blank>
-PASS Parsing: <file:///./Y> against <about:blank>
-PASS Parsing: <file:///./Y:> against <about:blank>
-FAIL Parsing: <\\\.\Y:> against <about:blank> assert_equals: failure should set href to input expected "\\\\\\.\\Y:" but got ""
-PASS Parsing: <file:///y:> against <about:blank>
-PASS Parsing: <file:///y:/> against <about:blank>
-PASS Parsing: <file:///./y> against <about:blank>
-PASS Parsing: <file:///./y:> against <about:blank>
-FAIL Parsing: <\\\.\y:> against <about:blank> assert_equals: failure should set href to input expected "\\\\\\.\\y:" but got ""
-FAIL Parsing: <file://localhost//a//../..//foo> against <about:blank> assert_equals: href expected "file://///foo" but got "file://localhost///foo"
-FAIL Parsing: <file://localhost////foo> against <about:blank> assert_equals: href expected "file://////foo" but got "file://localhost////foo"
-FAIL Parsing: <file:////foo> against <about:blank> assert_equals: href expected "file:////foo" but got "file:///foo"
-PASS Parsing: <file:///one/two> against <file:///>
-FAIL Parsing: <file:////one/two> against <file:///> assert_equals: href expected "file:////one/two" but got "file:///one/two"
-PASS Parsing: <//one/two> against <file:///>
-PASS Parsing: <///one/two> against <file:///>
-FAIL Parsing: <////one/two> against <file:///> assert_equals: href expected "file:////one/two" but got "file:///one/two"
-PASS Parsing: <file:///.//> against <file:////>
-PASS Parsing: <file:.//p> against <about:blank>
-PASS Parsing: <file:/.//p> against <about:blank>
-PASS Parsing: <http://[1:0::]> against <http://example.net/>
-FAIL Parsing: <http://[0:1:2:3:4:5:6:7:8]> against <http://example.net/> assert_equals: failure should set href to input expected "http://[0:1:2:3:4:5:6:7:8]" but got "http://[0:1:2:3:4:5:6:7:8]/"
-FAIL Parsing: <https://[0::0::0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0::0::0]" but got "https://[0::0::0]/"
-FAIL Parsing: <https://[0:.0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:.0]" but got "https://[0:.0]/"
-FAIL Parsing: <https://[0:0:]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:0:]" but got "https://[0:0:]/"
-FAIL Parsing: <https://[0:1:2:3:4:5:6:7.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1:2:3:4:5:6:7.0.0.0.1]" but got "https://[0:1:2:3:4:5:6:7.0.0.0.1]/"
-FAIL Parsing: <https://[0:1.00.0.0.0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1.00.0.0.0]" but got "https://[0:1.00.0.0.0]/"
-FAIL Parsing: <https://[0:1.290.0.0.0]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1.290.0.0.0]" but got "https://[0:1.290.0.0.0]/"
-FAIL Parsing: <https://[0:1.23.23]> against <about:blank> assert_equals: failure should set href to input expected "https://[0:1.23.23]" but got "https://[0:1.23.23]/"
-FAIL Parsing: <http://?> against <about:blank> assert_equals: failure should set href to input expected "http://?" but got "http:/?"
-FAIL Parsing: <http://#> against <about:blank> assert_equals: failure should set href to input expected "http://#" but got "http:/#"
-PASS Parsing: <http://f:4294967377/c> against <http://example.org/>
-PASS Parsing: <http://f:18446744073709551697/c> against <http://example.org/>
-PASS Parsing: <http://f:340282366920938463463374607431768211537/c> against <http://example.org/>
-FAIL Parsing: <sc://ñ> against <about:blank> assert_equals: host expected "%C3%B1" but got ""
-FAIL Parsing: <sc://ñ?x> against <about:blank> assert_equals: host expected "%C3%B1" but got ""
-FAIL Parsing: <sc://ñ#x> against <about:blank> assert_equals: host expected "%C3%B1" but got ""
-FAIL Parsing: <#x> against <sc://ñ> assert_equals: href expected "sc://%C3%B1#x" but got "sc://%C3%B1"
-FAIL Parsing: <?x> against <sc://ñ> assert_equals: href expected "sc://%C3%B1?x" but got "sc://%C3%B1"
-FAIL Parsing: <sc://?> against <about:blank> assert_equals: pathname expected "" but got "//"
-FAIL Parsing: <sc://#> against <about:blank> assert_equals: pathname expected "" but got "//"
-FAIL Parsing: <///> against <sc://x/> assert_equals: href expected "sc:///" but got "sc:"
-FAIL Parsing: <////> against <sc://x/> assert_equals: href expected "sc:////" but got "sc:"
-FAIL Parsing: <////x/> against <sc://x/> assert_equals: href expected "sc:////x/" but got "sc://x/"
-FAIL Parsing: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank> assert_equals: host expected "foobar.com" but got ""
-FAIL Parsing: <telnet://user:pass@foobar.com:23/> against <about:blank> assert_equals: username expected "user" but got ""
-FAIL Parsing: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank> assert_equals: host expected "10.10.10.10:7777" but got ""
-FAIL Parsing: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank> assert_equals: username expected "foo" but got ""
-FAIL Parsing: <rsync://foo@host:911/sup> against <about:blank> assert_equals: username expected "foo" but got ""
-FAIL Parsing: <git://github.com/foo/bar.git> against <about:blank> assert_equals: host expected "github.com" but got ""
-FAIL Parsing: <irc://myserver.com:6999/channel?passwd> against <about:blank> assert_equals: host expected "myserver.com:6999" but got ""
-FAIL Parsing: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank> assert_equals: host expected "fw.example.org:9999" but got ""
-FAIL Parsing: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank> assert_equals: host expected "localhost:389" but got ""
-FAIL Parsing: <git+https://github.com/foo/bar> against <about:blank> assert_equals: host expected "github.com" but got ""
-PASS Parsing: <urn:ietf:rfc:2648> against <about:blank>
-PASS Parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
-FAIL Parsing: <non-spec:/.//> against <about:blank> assert_equals: pathname expected "//" but got "/.//"
-FAIL Parsing: <non-spec:/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec:/..//"
-FAIL Parsing: <non-spec:/a/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec:/a/..//"
-FAIL Parsing: <non-spec:/.//path> against <about:blank> assert_equals: pathname expected "//path" but got "/.//path"
-FAIL Parsing: <non-spec:/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec:/..//path"
-FAIL Parsing: <non-spec:/a/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec:/a/..//path"
-FAIL Parsing: </.//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: </..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: <..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: <a/..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
-FAIL Parsing: <> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//p" but got "non-spec:/..//p"
-FAIL Parsing: <path> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//path" but got "non-spec:/..//path"
-FAIL Parsing: <../path> against <non-spec:/.//p> assert_equals: href expected "non-spec:/path" but got "non-spec:/./path"
-FAIL Parsing: <non-special://%E2%80%A0/> against <about:blank> assert_equals: host expected "%E2%80%A0" but got ""
-FAIL Parsing: <non-special://H%4fSt/path> against <about:blank> assert_equals: host expected "H%4fSt" but got ""
-FAIL Parsing: <non-special://[1:2:0:0:5:0:0:0]/> against <about:blank> assert_equals: href expected "non-special://[1:2:0:0:5::]/" but got "non-special://[1:2:0:0:5:0:0:0]/"
-FAIL Parsing: <non-special://[1:2:0:0:0:0:0:3]/> against <about:blank> assert_equals: href expected "non-special://[1:2::3]/" but got "non-special://[1:2:0:0:0:0:0:3]/"
-FAIL Parsing: <non-special://[1:2::3]:80/> against <about:blank> assert_equals: host expected "[1:2::3]:80" but got ""
-FAIL Parsing: <non-special://[:80/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <blob:https://example.com:443/> against <about:blank>
-PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
-PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
-PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
-FAIL Parsing: <http://[::127.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "http://[::127.0.0.0.1]" but got "http://[::127.0.0.0.1]/"
-PASS Parsing: <http://[0:1:0:1:0:1:0:1]> against <about:blank>
-PASS Parsing: <http://[1:0:1:0:1:0:1:0]> against <about:blank>
-PASS Parsing: <http://example.org/test?"> against <about:blank>
-PASS Parsing: <http://example.org/test?#> against <about:blank>
-PASS Parsing: <http://example.org/test?<> against <about:blank>
-PASS Parsing: <http://example.org/test?>> against <about:blank>
-PASS Parsing: <http://example.org/test?⌣> against <about:blank>
-PASS Parsing: <http://example.org/test?%23%23> against <about:blank>
-PASS Parsing: <http://example.org/test?%GH> against <about:blank>
-PASS Parsing: <http://example.org/test?a#%EF> against <about:blank>
-PASS Parsing: <http://example.org/test?a#%GH> against <about:blank>
-FAIL Parsing: <a> against <about:blank> assert_equals: failure should set href to input expected "a" but got ""
-FAIL Parsing: <a/> against <about:blank> assert_equals: failure should set href to input expected "a/" but got ""
-FAIL Parsing: <a//> against <about:blank> assert_equals: failure should set href to input expected "a//" but got ""
-FAIL Parsing: <test-a-colon.html> against <a:> assert_equals: failure should set href to input expected "test-a-colon.html" but got ""
-FAIL Parsing: <test-a-colon-b.html> against <a:b> assert_equals: failure should set href to input expected "test-a-colon-b.html" but got ""
-PASS Parsing: <test-a-colon-slash.html> against <a:/>
-FAIL Parsing: <test-a-colon-slash-slash.html> against <a://> assert_equals: href expected "a:///test-a-colon-slash-slash.html" but got ""
-PASS Parsing: <test-a-colon-slash-b.html> against <a:/b>
-FAIL Parsing: <test-a-colon-slash-slash-b.html> against <a://b> assert_equals: href expected "a://b/test-a-colon-slash-slash-b.html" but got "a://b"
-PASS Parsing: <http://example.org/test?a#b\0c> against <about:blank>
-FAIL Parsing: <non-spec://example.org/test?a#b\0c> against <about:blank> assert_equals: host expected "example.org" but got ""
-PASS Parsing: <non-spec:/test?a#b\0c> against <about:blank>
-PASS Parsing: <10.0.0.7:8080/foo.html> against <file:///some/dir/bar.html>
-PASS Parsing: <a!@$*=/foo.html> against <file:///some/dir/bar.html>
-PASS Parsing: <a1234567890-+.:foo/bar> against <http://example.com/dir/file>
-PASS Parsing: <file://a­b/p> against <about:blank>
-PASS Parsing: <file://a%C2%ADb/p> against <about:blank>
-FAIL Parsing: <file://­/p> against <about:blank> assert_equals: failure should set href to input expected "file://­/p" but got "file://%C2%AD/p"
-PASS Parsing: <file://%C2%AD/p> against <about:blank>
-FAIL Parsing: <file://xn--/p> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
-PASS Parsing: <#link> against <https://example.org/##link>
-PASS Parsing: <non-special:cannot-be-a-base-url-\0~€> against <about:blank>
-PASS Parsing: <https://www.example.com/path{path.html?query'=query#fragment<fragment> against <about:blank>
-PASS Parsing: <https://user:pass[@foo/bar> against <http://example.org>
-FAIL Parsing: <foo:// !"$%&'()*+,-.;<=>@[\]^_`{|}~@host/> against <about:blank> assert_equals: href expected "foo://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/" but got "foo:// !\"$%&'()*+,-.;<=>@[\\]^_`{|}~@host/"
-FAIL Parsing: <wss:// !"$%&'()*+,-.;<=>@[]^_`{|}~@host/> against <about:blank> assert_equals: href expected "wss://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/" but got "wss://%20!%22$%&%27()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/"
-FAIL Parsing: <foo://joe: !"$%&'()*+,-.:;<=>@[\]^_`{|}~@host/> against <about:blank> assert_equals: href expected "foo://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/" but got "foo://joe: !\"$%&'()*+,-.:;<=>@[\\]^_`{|}~@host/"
-FAIL Parsing: <wss://joe: !"$%&'()*+,-.:;<=>@[]^_`{|}~@host/> against <about:blank> assert_equals: href expected "wss://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/" but got "wss://joe:%20!%22$%&%27()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/"
-FAIL Parsing: <foo://!"$%&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: host expected "!\"$%&'()*+,-.;=_`{}~" but got ""
-FAIL Parsing: <wss://!"$&'()*+,-.;=_`{}~/> against <about:blank> assert_equals: href expected "wss://!\"$&'()*+,-.;=_`{}~/" but got "wss://%21%22%24%26%27%28%29%2A+%2C-.%3B%3D_%60%7B%7D%7E/"
-FAIL Parsing: <foo://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank> assert_equals: href expected "foo://host/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~" but got "foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~"
-FAIL Parsing: <wss://host/ !"$%&'()*+,-./:;<=>@[\]^_`{|}~> against <about:blank> assert_equals: href expected "wss://host/%20!%22$%&'()*+,-./:;%3C=%3E@[/]^_%60%7B|%7D~" but got "wss://host/%20!%22$%&'()*+,-./:;%3C=%3E@[/]%5E_%60%7B%7C%7D~"
-FAIL Parsing: <foo://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank> assert_equals: href expected "foo://host/dir/?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~" but got "foo://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
-PASS Parsing: <wss://host/dir/? !"$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-FAIL Parsing: <foo://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank> assert_equals: host expected "host" but got ""
-PASS Parsing: <wss://host/dir/# !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~> against <about:blank>
-FAIL Parsing: <abc:rootless> against <abc://host/path> assert_equals: href expected "abc:rootless" but got "abc://host/rootless"
-FAIL Parsing: <abc:rootless> against <abc:/path> assert_equals: href expected "abc:rootless" but got "abc:/rootless"
-PASS Parsing: <abc:rootless> against <abc:path>
-FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
index 762c7838..00d227d 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 633 tests; 376 PASS, 257 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 659 tests; 377 PASS, 282 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -297,7 +297,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 FAIL Parsing: <http://[google.com]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[google.com]" but got "http://[google.com]/"
 FAIL Parsing: <http://[::1.2.3.4x]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[::1.2.3.4x]" but got "http://[::1.2.3.4x]/"
@@ -421,11 +420,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 FAIL Parsing: <http://10000000000> against <http://other.com/> assert_equals: failure should set href to input expected "http://10000000000" but got "http://10000000000/"
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -434,7 +437,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 FAIL Parsing: <http://0xffffffff1> against <http://other.com/> assert_equals: failure should set href to input expected "http://0xffffffff1" but got "http://0xffffffff1/"
 FAIL Parsing: <http://256.256.256.256> against <http://other.com/> assert_equals: failure should set href to input expected "http://256.256.256.256" but got "http://256.256.256.256/"
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -589,7 +591,6 @@
 FAIL Parsing: <non-special://[:80/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 FAIL Parsing: <http://[::127.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "http://[::127.0.0.0.1]" but got "http://[::127.0.0.0.1]/"
@@ -644,5 +645,30 @@
 FAIL Parsing: <abc:rootless> against <abc:/path> assert_equals: href expected "abc:rootless" but got "abc:/rootless"
 PASS Parsing: <abc:rootless> against <abc:path>
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0x100.2.3.4> against <about:blank> assert_equals: failure should set href to input expected "http://0x100.2.3.4" but got "http://0x100.2.3.4/"
+FAIL Parsing: <http://0x100.2.3.4.> against <about:blank> assert_equals: failure should set href to input expected "http://0x100.2.3.4." but got "http://0x100.2.3.4./"
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.09> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt
index 21264a7..e5fd1f4 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 485 tests; 265 PASS, 220 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 611 tests; 275 PASS, 336 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL's constructor's base argument: file://example:1/ should throw
 PASS URL's href: file://example:1/ should throw
@@ -485,5 +485,131 @@
 PASS sendBeacon(): file://xn--/p should throw
 FAIL Location's href: file://xn--/p should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
 FAIL window.open(): file://xn--/p should throw assert_throws_dom: function "() => self.open(test.input).close()" threw object "TypeError: Cannot read properties of null (reading 'close')" that is not a DOMException SyntaxError: property "code" is equal to undefined, expected 12
+FAIL URL's constructor's base argument: http://0..0x300/ should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0..0x300/ should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0..0x300/ should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0..0x300/ should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0..0x300/ should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0..0x300/ should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://0..0x300./ should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0..0x300./ should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0..0x300./ should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0..0x300./ should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0..0x300./ should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0..0x300./ should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.08 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.08 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.08 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.08 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.08 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.08 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.08. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.08. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.08. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.08. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.08. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.08. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.09 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.09 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.09 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.09 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.09 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.09 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://09.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://09.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://09.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://09.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://09.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://09.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://09.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://09.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://09.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://09.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://09.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://09.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://01.2.3.4.5 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://01.2.3.4.5 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://01.2.3.4.5 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://01.2.3.4.5 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://01.2.3.4.5 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://01.2.3.4.5 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://01.2.3.4.5. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://01.2.3.4.5. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://01.2.3.4.5. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://01.2.3.4.5. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://01.2.3.4.5. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://01.2.3.4.5. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+PASS URL's constructor's base argument: http://0x100.2.3.4 should throw
+PASS URL's href: http://0x100.2.3.4 should throw
+PASS XHR: http://0x100.2.3.4 should throw
+PASS sendBeacon(): http://0x100.2.3.4 should throw
+FAIL Location's href: http://0x100.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" threw object "SyntaxError: Failed to set the 'href' property on 'Location': 'http://0x100.2.3.4' is not a valid URL." ("SyntaxError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+PASS window.open(): http://0x100.2.3.4 should throw
+PASS URL's constructor's base argument: http://0x100.2.3.4. should throw
+PASS URL's href: http://0x100.2.3.4. should throw
+PASS XHR: http://0x100.2.3.4. should throw
+PASS sendBeacon(): http://0x100.2.3.4. should throw
+FAIL Location's href: http://0x100.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" threw object "SyntaxError: Failed to set the 'href' property on 'Location': 'http://0x100.2.3.4.' is not a valid URL." ("SyntaxError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+PASS window.open(): http://0x100.2.3.4. should throw
+FAIL URL's constructor's base argument: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0x1.2.3.4.5 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0x1.2.3.4.5 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0x1.2.3.4.5 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0x1.2.3.4.5. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0x1.2.3.4.5. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0x1.2.3.4.5. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.1.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.1.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.1.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.1.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.1.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.1.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.1.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.1.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.1.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.1.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.1.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.1.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.09 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.09 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.09 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.09 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.09 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.09 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.09. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.09. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.09. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.09. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.09. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.09. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.0x4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.0x4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.0x4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.0x4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.0x4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.0x4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.0x4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.0x4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.0x4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.0x4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.0x4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.0x4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt
index eb20797..e931b952 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 452 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 661 tests; 455 PASS, 206 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -317,7 +317,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 PASS Parsing: <http://[google.com]> against <http://other.com/>
 PASS Parsing: <http://[::1.2.3.4x]> against <http://other.com/>
@@ -491,11 +490,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 PASS Parsing: <http://10000000000> against <http://other.com/>
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -504,7 +507,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 PASS Parsing: <http://0xffffffff1> against <http://other.com/>
 PASS Parsing: <http://256.256.256.256> against <http://other.com/>
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -673,7 +675,6 @@
         }" did not throw
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 PASS Parsing: <http://[::127.0.0.0.1]> against <about:blank>
@@ -732,5 +733,76 @@
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
 PASS Parsing: <#> against <null>
 PASS Parsing: <?> against <null>
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+PASS Parsing: <http://0x100.2.3.4> against <about:blank>
+PASS Parsing: <http://0x100.2.3.4.> against <about:blank>
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any.worker-expected.txt
index eb20797..e931b952 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any.worker-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 452 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 661 tests; 455 PASS, 206 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -317,7 +317,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 PASS Parsing: <http://[google.com]> against <http://other.com/>
 PASS Parsing: <http://[::1.2.3.4x]> against <http://other.com/>
@@ -491,11 +490,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 PASS Parsing: <http://10000000000> against <http://other.com/>
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -504,7 +507,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 PASS Parsing: <http://0xffffffff1> against <http://other.com/>
 PASS Parsing: <http://256.256.256.256> against <http://other.com/>
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -673,7 +675,6 @@
         }" did not throw
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 PASS Parsing: <http://[::127.0.0.0.1]> against <about:blank>
@@ -732,5 +733,76 @@
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
 PASS Parsing: <#> against <null>
 PASS Parsing: <?> against <null>
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+PASS Parsing: <http://0x100.2.3.4> against <about:blank>
+PASS Parsing: <http://0x100.2.3.4.> against <about:blank>
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
index 65ebbc32..ac8155bb 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 633 tests; 360 PASS, 273 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 659 tests; 361 PASS, 298 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -297,7 +297,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 FAIL Parsing: <http://[google.com]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[google.com]" but got "http://[google.com]/"
 FAIL Parsing: <http://[::1.2.3.4x]> against <http://other.com/> assert_equals: failure should set href to input expected "http://[::1.2.3.4x]" but got "http://[::1.2.3.4x]/"
@@ -421,11 +420,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 FAIL Parsing: <http://10000000000> against <http://other.com/> assert_equals: failure should set href to input expected "http://10000000000" but got "http://10000000000/"
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -434,7 +437,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 FAIL Parsing: <http://0xffffffff1> against <http://other.com/> assert_equals: failure should set href to input expected "http://0xffffffff1" but got "http://0xffffffff1/"
 FAIL Parsing: <http://256.256.256.256> against <http://other.com/> assert_equals: failure should set href to input expected "http://256.256.256.256" but got "http://256.256.256.256/"
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -589,7 +591,6 @@
 FAIL Parsing: <non-special://[:80/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 FAIL Parsing: <http://[::127.0.0.0.1]> against <about:blank> assert_equals: failure should set href to input expected "http://[::127.0.0.0.1]" but got "http://[::127.0.0.0.1]/"
@@ -644,5 +645,30 @@
 FAIL Parsing: <abc:rootless> against <abc:/path> assert_equals: href expected "abc:rootless" but got "abc:/rootless"
 PASS Parsing: <abc:rootless> against <abc:path>
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0x100.2.3.4> against <about:blank> assert_equals: failure should set href to input expected "http://0x100.2.3.4" but got "http://0x100.2.3.4/"
+FAIL Parsing: <http://0x100.2.3.4.> against <about:blank> assert_equals: failure should set href to input expected "http://0x100.2.3.4." but got "http://0x100.2.3.4./"
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.09> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt
index 42c34f9..f6f037b2 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 485 tests; 261 PASS, 224 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 611 tests; 271 PASS, 340 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL's constructor's base argument: file://example:1/ should throw
 PASS URL's href: file://example:1/ should throw
@@ -485,5 +485,131 @@
 PASS sendBeacon(): file://xn--/p should throw
 FAIL Location's href: file://xn--/p should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
 FAIL window.open(): file://xn--/p should throw assert_throws_dom: function "() => self.open(test.input).close()" threw object "TypeError: Cannot read properties of null (reading 'close')" that is not a DOMException SyntaxError: property "code" is equal to undefined, expected 12
+FAIL URL's constructor's base argument: http://0..0x300/ should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0..0x300/ should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0..0x300/ should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0..0x300/ should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0..0x300/ should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0..0x300/ should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://0..0x300./ should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0..0x300./ should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0..0x300./ should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0..0x300./ should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0..0x300./ should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0..0x300./ should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.08 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.08 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.08 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.08 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.08 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.08 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.08. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.08. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.08. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.08. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.08. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.08. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://1.2.3.09 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://1.2.3.09 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://1.2.3.09 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://1.2.3.09 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://1.2.3.09 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://1.2.3.09 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://09.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://09.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://09.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://09.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://09.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://09.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://09.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://09.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://09.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://09.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://09.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://09.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://01.2.3.4.5 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://01.2.3.4.5 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://01.2.3.4.5 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://01.2.3.4.5 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://01.2.3.4.5 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://01.2.3.4.5 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://01.2.3.4.5. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://01.2.3.4.5. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://01.2.3.4.5. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://01.2.3.4.5. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://01.2.3.4.5. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://01.2.3.4.5. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+PASS URL's constructor's base argument: http://0x100.2.3.4 should throw
+PASS URL's href: http://0x100.2.3.4 should throw
+PASS XHR: http://0x100.2.3.4 should throw
+PASS sendBeacon(): http://0x100.2.3.4 should throw
+FAIL Location's href: http://0x100.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" threw object "SyntaxError: Failed to set the 'href' property on 'Location': 'http://0x100.2.3.4' is not a valid URL." ("SyntaxError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+PASS window.open(): http://0x100.2.3.4 should throw
+PASS URL's constructor's base argument: http://0x100.2.3.4. should throw
+PASS URL's href: http://0x100.2.3.4. should throw
+PASS XHR: http://0x100.2.3.4. should throw
+PASS sendBeacon(): http://0x100.2.3.4. should throw
+FAIL Location's href: http://0x100.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" threw object "SyntaxError: Failed to set the 'href' property on 'Location': 'http://0x100.2.3.4.' is not a valid URL." ("SyntaxError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+PASS window.open(): http://0x100.2.3.4. should throw
+FAIL URL's constructor's base argument: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0x1.2.3.4.5 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0x1.2.3.4.5 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0x1.2.3.4.5 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0x1.2.3.4.5 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://0x1.2.3.4.5. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://0x1.2.3.4.5. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://0x1.2.3.4.5. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://0x1.2.3.4.5. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.1.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.1.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.1.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.1.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.1.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.1.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.1.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.1.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.1.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.1.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.1.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.1.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.2.3.4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.2.3.4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.2.3.4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.2.3.4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.2.3.4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.2.3.4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.2.3.4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.2.3.4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.2.3.4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.2.3.4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.2.3.4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.2.3.4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.09 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.09 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.09 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.09 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.09 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.09 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.09. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.09. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.09. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.09. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.09. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.09. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.0x4 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.0x4 should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.0x4 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.0x4 should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.0x4 should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.0x4 should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://foo.0x4. should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://foo.0x4. should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://foo.0x4. should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://foo.0x4. should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://foo.0x4. should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://foo.0x4. should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt
index 8c28967..be7edeca 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 432 PASS, 203 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 661 tests; 435 PASS, 226 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -317,7 +317,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 PASS Parsing: <http://[google.com]> against <http://other.com/>
 PASS Parsing: <http://[::1.2.3.4x]> against <http://other.com/>
@@ -491,11 +490,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 PASS Parsing: <http://10000000000> against <http://other.com/>
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -504,7 +507,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 PASS Parsing: <http://0xffffffff1> against <http://other.com/>
 PASS Parsing: <http://256.256.256.256> against <http://other.com/>
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -677,7 +679,6 @@
         }" did not throw
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 PASS Parsing: <http://[::127.0.0.0.1]> against <about:blank>
@@ -740,5 +741,76 @@
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
 PASS Parsing: <#> against <null>
 PASS Parsing: <?> against <null>
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+PASS Parsing: <http://0x100.2.3.4> against <about:blank>
+PASS Parsing: <http://0x100.2.3.4.> against <about:blank>
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any.worker-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any.worker-expected.txt
index 8c28967..be7edeca 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any.worker-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 432 PASS, 203 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 661 tests; 435 PASS, 226 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -317,7 +317,6 @@
 PASS Parsing: <http://0Xc0.0250.01> against <http://other.com/>
 PASS Parsing: <http://./> against <about:blank>
 PASS Parsing: <http://../> against <about:blank>
-PASS Parsing: <http://0..0x300/> against <about:blank>
 PASS Parsing: <http://[www.google.com]/> against <about:blank>
 PASS Parsing: <http://[google.com]> against <http://other.com/>
 PASS Parsing: <http://[::1.2.3.4x]> against <http://other.com/>
@@ -491,11 +490,15 @@
 PASS Parsing: <http://foo.bar/baz?qux#foo<bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo>bar> against <about:blank>
 PASS Parsing: <http://foo.bar/baz?qux#foo`bar> against <about:blank>
+PASS Parsing: <http://1.2.3.4/> against <http://other.com/>
+PASS Parsing: <http://1.2.3.4./> against <http://other.com/>
 PASS Parsing: <http://192.168.257> against <http://other.com/>
+PASS Parsing: <http://192.168.257.> against <http://other.com/>
 PASS Parsing: <http://192.168.257.com> against <http://other.com/>
 PASS Parsing: <http://256> against <http://other.com/>
 PASS Parsing: <http://256.com> against <http://other.com/>
 PASS Parsing: <http://999999999> against <http://other.com/>
+PASS Parsing: <http://999999999.> against <http://other.com/>
 PASS Parsing: <http://999999999.com> against <http://other.com/>
 PASS Parsing: <http://10000000000> against <http://other.com/>
 PASS Parsing: <http://10000000000.com> against <http://other.com/>
@@ -504,7 +507,6 @@
 PASS Parsing: <http://0xffffffff> against <http://other.com/>
 PASS Parsing: <http://0xffffffff1> against <http://other.com/>
 PASS Parsing: <http://256.256.256.256> against <http://other.com/>
-PASS Parsing: <http://256.256.256.256.256> against <http://other.com/>
 PASS Parsing: <https://0x.0x.0> against <about:blank>
 PASS Parsing: <https://0x100000000/test> against <about:blank>
 PASS Parsing: <https://256.0.0.1/test> against <about:blank>
@@ -677,7 +679,6 @@
         }" did not throw
 PASS Parsing: <blob:https://example.com:443/> against <about:blank>
 PASS Parsing: <blob:d3958f5c-0777-0845-9dcf-2cb28783acaf> against <about:blank>
-PASS Parsing: <http://0177.0.0.0189> against <about:blank>
 PASS Parsing: <http://0x7f.0.0.0x7g> against <about:blank>
 PASS Parsing: <http://0X7F.0.0.0X7G> against <about:blank>
 PASS Parsing: <http://[::127.0.0.0.1]> against <about:blank>
@@ -740,5 +741,76 @@
 FAIL Parsing: <abc:/rooted> against <abc://host/path> assert_equals: href expected "abc:/rooted" but got "abc://host/rooted"
 PASS Parsing: <#> against <null>
 PASS Parsing: <?> against <null>
+FAIL Parsing: <http://1.2.3.4.5> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.4.5.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300/> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0..0x300./> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://256.256.256.256.256.> against <http://other.com/> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.08.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://1.2.3.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://09.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://01.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+PASS Parsing: <http://0x100.2.3.4> against <about:blank>
+PASS Parsing: <http://0x100.2.3.4.> against <about:blank>
+FAIL Parsing: <http://0x1.2.3.4.5> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://0x1.2.3.4.5.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.1.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.2.3.4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.09.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://foo.0x4.> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png b/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png
index 07bb176..2f6af964b 100644
--- a/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png
+++ b/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png
Binary files differ
diff --git a/third_party/highway/README.chromium b/third_party/highway/README.chromium
index 753f481..a99e0fb 100644
--- a/third_party/highway/README.chromium
+++ b/third_party/highway/README.chromium
@@ -1,9 +1,9 @@
 Name: Highway: C++ library for SIMD
 Short Name: highway
 URL: https://github.com/google/highway
-Version: 0.12.0
-Date: 2020-04-15
-Revision: ca1a57c342cd815053abfcffa29b44eaead4f20b
+Version: 0.12.2
+Date: 2020-05-31
+Revision: 424360251cdcfc314cfc528f53c872ecd63af0f0
 License: Apache 2.0
 Security Critical: yes
 CPEPrefix: unknown
diff --git a/third_party/libjxl/LICENSE b/third_party/libjxl/LICENSE
index 6b0b127..c66034b 100644
--- a/third_party/libjxl/LICENSE
+++ b/third_party/libjxl/LICENSE
@@ -1,203 +1,27 @@
+Copyright (c) the JPEG XL Project Authors.
+All rights reserved.
 
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
 
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
 
-   1. Definitions.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
 
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
 
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/libjxl/README.chromium b/third_party/libjxl/README.chromium
index 45777cf..2dd6e1b 100644
--- a/third_party/libjxl/README.chromium
+++ b/third_party/libjxl/README.chromium
@@ -1,16 +1,15 @@
 Name: JPEG XL image decoder library
 Short Name: libjxl
-URL: https://gitlab.com/wg1/jpeg-xl
-Version: 0
-Date: 2021-06-14
-Revision: 4a981fd8be383703ca8c5dc78c25411c14a01d9f
-License: Apache 2.0
+URL: https://github.com/libjxl/libjxl
+Version: 0.5
+Date: 2021-08-02
+Revision: c4e0877f93506e880cd922f6c94644d79ae9adff
+License: BSD 3-Clause
 Security Critical: yes
-CPEPrefix: unknown
+CPEPrefix: cpe:2.3:a:libjxl_project:libjxl:0.5
 
 Description:
 The reference implementation for the JPEG XL image encoder/decoder.
 
 Local Modifications:
 None. Only decoder-side is compiled.
-
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/orderfile_generator_backend.py
index 9beefc6..b9f0200 100755
--- a/tools/cygprofile/orderfile_generator_backend.py
+++ b/tools/cygprofile/orderfile_generator_backend.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env vpython
+#!/usr/bin/env vpython3
 # Copyright (c) 2013 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.
@@ -13,7 +13,6 @@
   tools/cygprofile/orderfile_generator_backend.py --use-goma --target-arch=arm
 """
 
-from __future__ import print_function
 
 import argparse
 import csv
@@ -388,7 +387,7 @@
       Exception if the hash file does not match the file.
       NotImplementedError when the commit logic hasn't been overridden.
     """
-    files_to_commit = list(filter(None, files))
+    files_to_commit = [_f for _f in files if _f]
     if files_to_commit:
       self._CommitStashedFiles(files_to_commit)
 
@@ -964,8 +963,8 @@
     elif self._options.manual_symbol_offsets:
       assert self._options.manual_libname
       assert self._options.manual_objdir
-      with file(self._options.manual_symbol_offsets) as f:
-        symbol_offsets = [int(x) for x in f.xreadlines()]
+      with open(self._options.manual_symbol_offsets) as f:
+        symbol_offsets = [int(x) for x in f]
       processor = process_profiles.SymbolOffsetProcessor(
           self._compiler.manual_libname)
       generator = cyglog_to_orderfile.OffsetOrderfileGenerator(
diff --git a/tools/cygprofile/profile_android_startup.py b/tools/cygprofile/profile_android_startup.py
index 1068716..0eb9577 100755
--- a/tools/cygprofile/profile_android_startup.py
+++ b/tools/cygprofile/profile_android_startup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env vpython
+#!/usr/bin/env vpython3
 # Copyright (c) 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -10,7 +10,6 @@
 to make runs repeatable.
 """
 
-from __future__ import print_function
 
 import argparse
 import logging
@@ -529,7 +528,7 @@
 
   apk = apk_helper.ApkHelper(args.apk_path)
   package_info = None
-  for p in constants.PACKAGE_INFO.itervalues():
+  for p in constants.PACKAGE_INFO.items():
     if p.package == apk.GetPackageName():
       package_info = p
       break
diff --git a/tools/dump_process_memory/collect_process_dump.py b/tools/dump_process_memory/collect_process_dump.py
index 47f4b6e..ba348fa 100755
--- a/tools/dump_process_memory/collect_process_dump.py
+++ b/tools/dump_process_memory/collect_process_dump.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2019 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index ff9faf82..62499c6 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -206,8 +206,8 @@
     },
 
     'chromium.dawn': {
-      'Dawn Linux x64 Builder': 'dawn_tests_release_trybot',
-      'Dawn Linux x64 DEPS Builder': 'dawn_tests_release_trybot',
+      'Dawn Linux x64 Builder': 'dawn_tests_with_desktop_gl_release_trybot',
+      'Dawn Linux x64 DEPS Builder': 'dawn_tests_with_desktop_gl_release_trybot',
 
       'Dawn Mac x64 Builder': 'dawn_tests_release_trybot',
       'Dawn Mac x64 DEPS Builder': 'dawn_tests_release_trybot',
@@ -962,12 +962,12 @@
     },
 
     'tryserver.chromium.dawn': {
-      'dawn-linux-x64-deps-rel': 'dawn_tests_release_trybot',
+      'dawn-linux-x64-deps-rel': 'dawn_tests_with_desktop_gl_release_trybot',
       'dawn-mac-x64-deps-rel': 'dawn_tests_release_trybot',
       'dawn-try-mac-amd-exp': 'dawn_tests_release_trybot',
       'dawn-win10-x86-deps-rel': 'dawn_tests_release_trybot_x86',
       'dawn-win10-x64-deps-rel': 'dawn_tests_release_trybot',
-      'linux-dawn-rel': 'dawn_tests_release_trybot',
+      'linux-dawn-rel': 'dawn_tests_with_desktop_gl_release_trybot',
       'mac-dawn-rel': 'dawn_tests_release_trybot',
       'win-dawn-rel': 'dawn_tests_release_trybot',
       'dawn-try-win10-x86-rel': 'dawn_tests_release_trybot_x86',
@@ -2022,6 +2022,10 @@
       'dawn_tests', 'release_trybot', 'x86',
     ],
 
+    'dawn_tests_with_desktop_gl_release_trybot': [
+      'dawn_tests', 'dawn_enable_desktop_gl', 'release_trybot',
+    ],
+
     'debug_bot': [
       'debug_bot',
     ],
@@ -3120,7 +3124,11 @@
     },
 
     'dawn_tests': {
-      'gn_args': 'use_dawn=true',
+      'gn_args': 'use_dawn=true dawn_enable_opengles=true',
+    },
+
+    'dawn_enable_desktop_gl': {
+      'gn_args': 'dawn_enable_desktop_gl=true',
     },
 
     'dcheck_always_on': {
diff --git a/tools/mb/mb_config_expectations/chromium.dawn.json b/tools/mb/mb_config_expectations/chromium.dawn.json
index 9edb80e4..62fc4399 100644
--- a/tools/mb/mb_config_expectations/chromium.dawn.json
+++ b/tools/mb/mb_config_expectations/chromium.dawn.json
@@ -2,6 +2,8 @@
   "Dawn Linux x64 Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_desktop_gl": true,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -13,6 +15,8 @@
   "Dawn Linux x64 DEPS Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_desktop_gl": true,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -24,6 +28,7 @@
   "Dawn Mac x64 Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -35,6 +40,7 @@
   "Dawn Mac x64 DEPS Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -46,6 +52,7 @@
   "Dawn Win10 x64 ASAN Release": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_asan": true,
       "is_component_build": false,
@@ -58,6 +65,7 @@
   "Dawn Win10 x64 Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -69,6 +77,7 @@
   "Dawn Win10 x64 DEPS Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -80,6 +89,7 @@
   "Dawn Win10 x86 Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -92,6 +102,7 @@
   "Dawn Win10 x86 DEPS Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json b/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
index 711d4ce..95b3f5de 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
@@ -2,6 +2,8 @@
   "dawn-linux-x64-deps-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_desktop_gl": true,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -13,6 +15,7 @@
   "dawn-mac-x64-deps-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -24,6 +27,7 @@
   "dawn-try-mac-amd-exp": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -35,6 +39,7 @@
   "dawn-try-win10-x64-asan-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_asan": true,
       "is_component_build": false,
@@ -47,6 +52,7 @@
   "dawn-try-win10-x86-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -59,6 +65,7 @@
   "dawn-win10-x64-deps-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -70,6 +77,7 @@
   "dawn-win10-x86-deps-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -82,6 +90,8 @@
   "linux-dawn-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_desktop_gl": true,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -93,6 +103,7 @@
   "mac-dawn-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
@@ -104,6 +115,7 @@
   "win-dawn-rel": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
+      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "is_component_build": false,
       "is_debug": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7bba660..7c36fe67 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -44447,6 +44447,13 @@
   <int value="2" label="BO"/>
 </enum>
 
+<enum name="LacrosLaunchMode">
+  <int value="0" label="Only Ash browser"/>
+  <int value="1" label="Ash and Lacros browser available"/>
+  <int value="2" label="Lacros browser is primary"/>
+  <int value="3" label="Only Lacros browser"/>
+</enum>
+
 <enum name="LanguageDetectionModelState">
   <int value="0" label="Unknown"/>
   <int value="1" label="Model File Invalid"/>
@@ -45405,6 +45412,20 @@
   <int value="11" label="Success: Image is translatable"/>
 </enum>
 
+<enum name="LensRegionSearchAspectRatio">
+  <summary>
+    Enum describing the aspect ratio of the captured region. The aspect ratios
+    are defined arbitrarly as follows: SQUARE: [0.8, 1.2] WIDE: (1.2, 1.7]
+    VERY_WIDE: (1.7, infinity] TALL: [0.3, 0.8) VERY_TALL: [0, 0.3)
+  </summary>
+  <int value="0" label="Undefined Aspect Ratio"/>
+  <int value="1" label="Square Aspect Ratio"/>
+  <int value="2" label="Wide Aspect Ratio"/>
+  <int value="3" label="Very Wide Aspect Ratio"/>
+  <int value="4" label="Tall Aspect Ratio"/>
+  <int value="5" label="Very Tall Aspect Ratio"/>
+</enum>
+
 <enum name="LensRegionSearchCaptureResult">
   <summary>Result of Lens Region Search feature.</summary>
   <int value="0" label="Success"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index c8229fc..dca8ed9 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -1510,7 +1510,7 @@
 </histogram>
 
 <histogram name="DomDistiller.InfoBarUsage" enum="BooleanUsage"
-    expires_after="M77">
+    expires_after="2022-01-01">
   <owner>mdjones@chromium.org</owner>
   <summary>
     &quot;Used&quot; is recorded when the user clicks the infobar to enter
@@ -1583,7 +1583,7 @@
 </histogram>
 
 <histogram name="DomDistiller.ReaderShownForPageLoad" enum="Boolean"
-    expires_after="M77">
+    expires_after="2022-01-01">
   <owner>mdjones@chromium.org</owner>
   <summary>
     Records if the panel became visible at any point after a page was navigated.
@@ -1701,7 +1701,7 @@
 </histogram>
 
 <histogram name="DomDistiller.Time.ViewingReaderModePage" units="ms"
-    expires_after="M77">
+    expires_after="2022-01-01">
   <owner>mdjones@chromium.org</owner>
   <summary>
     Records the amount of time a user spent on a Reader Mode Page.
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 5770d371..e61c39e9 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1419,6 +1419,17 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Lacros.Launch.Mode" enum="LacrosLaunchMode"
+    expires_after="2022-09-01">
+  <owner>skuhne@chromium.org</owner>
+  <owner>lacros-team@google.com</owner>
+  <summary>
+    The Lacros operation mode. This will record if Ash is the only browser, both
+    browsers are running side by side or if Lacros is the only browser. It will
+    be emitted once when the system (Ash) starts.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Login.Lock.AuthMethod.Switched"
     enum="AuthMethodSwitchType" expires_after="2022-06-29">
   <owner>rsorokin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 882ceeb3..80a0cec 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -8172,14 +8172,6 @@
   <affected-histogram name="Media.VideoCaptureManager"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="MediaVideoCategories" separator=".">
-  <suffix name="All" label="All media with a video track."/>
-  <suffix name="EME" label="EME media with a video track."/>
-  <suffix name="MSE" label="MSE media with a video track."/>
-  <suffix name="SRC" label="SRC media with a video track."/>
-  <affected-histogram name="Media.VideoHeight.Initial"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="MediaWatchTimeCategories" separator=".">
   <suffix name="Audio.AC"
       label="Watch time for all media with only an audio track on AC power."/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 98002db..6c48999 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -4737,14 +4737,20 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Media.VideoHeight.Initial" units="pixels"
-    expires_after="2021-08-22">
+<histogram name="Media.VideoHeight.Initial.{PlaybackType}" units="pixels"
+    expires_after="2022-08-22">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
-    The height of the first video frame in an HTML5 video. Reported when the
-    first video frame is available.
+    The height of the first video frame in an HTML5 video for {PlaybackType}
+    playbacks. Reported when the first video frame is available.
   </summary>
+  <token key="PlaybackType">
+    <variant name="All" summary="all"/>
+    <variant name="EME"/>
+    <variant name="MSE"/>
+    <variant name="SRC"/>
+  </token>
 </histogram>
 
 <histogram name="Media.VideoPersistence.AttemptResult"
@@ -5604,12 +5610,40 @@
   <owner>openscreen-eng@google.com</owner>
   <summary>
     Counts the number of devices known and populated to the Media Router dialog
-    three seconds after the dialog loads. Always expected to be non-negative.
-    This includes data recorded from Clank, which uses Android's MediaRouter
-    framework.
+    (or the Global Media Controls' device picker) three seconds after the dialog
+    loads. Always expected to be non-negative. This includes data recorded from
+    Clank, which uses Android's MediaRouter framework.
   </summary>
 </histogram>
 
+<histogram name="MediaRouter.Ui.Device.Count.{Ui}.{Trigger}.{Mrp}.{State}"
+    units="units" expires_after="2022-02-01">
+  <owner>takumif@chromium.org</owner>
+  <owner>openscreen-eng@google.com</owner>
+  <summary>
+    Counts the number of {Mrp} devices that are shown as {State} in the {Ui} UI
+    three seconds after the UI was opened via {Trigger}. Always expected to be
+    non-negative.
+  </summary>
+  <token key="Ui">
+    <variant name="CastHarmony"/>
+    <variant name="GlobalMediaControls"/>
+  </token>
+  <token key="Trigger">
+    <variant name="BrowserUi"/>
+    <variant name="PresentationApi"/>
+  </token>
+  <token key="Mrp">
+    <variant name="CAST"/>
+    <variant name="DIAL"/>
+    <variant name="WIRED_DISPLAY"/>
+  </token>
+  <token key="State">
+    <variant name="Available"/>
+    <variant name="Unavailable"/>
+  </token>
+</histogram>
+
 <histogram name="MediaRouter.Ui.Dialog.ActivationLocationAndCastMode"
     enum="MediaRouterDialogActivationLocationAndCastMode"
     expires_after="2022-02-01">
diff --git a/tools/metrics/histograms/metadata/mobile/OWNERS b/tools/metrics/histograms/metadata/mobile/OWNERS
index f3014b8..7b2b7d4 100644
--- a/tools/metrics/histograms/metadata/mobile/OWNERS
+++ b/tools/metrics/histograms/metadata/mobile/OWNERS
@@ -3,3 +3,4 @@
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
 ender@google.com
+mthiesse@chromium.org
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index ab9b451..e1111bc9 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -2306,7 +2306,7 @@
 </histogram>
 
 <histogram name="Net.NetworkErrorLogging.SignedExchangeRequestOutcome"
-    enum="NetNetworkErrorLoggingRequestOutcome" expires_after="M94">
+    enum="NetNetworkErrorLoggingRequestOutcome" expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index a0232d3..9d318e1e 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -12376,7 +12376,7 @@
 </histogram>
 
 <histogram name="PrefetchedSignedExchangeCache.BodySize" units="bytes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -12387,7 +12387,7 @@
 </histogram>
 
 <histogram name="PrefetchedSignedExchangeCache.BodySizeTotal" units="bytes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -12399,7 +12399,7 @@
 </histogram>
 
 <histogram name="PrefetchedSignedExchangeCache.Count" units="count"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -12411,7 +12411,7 @@
 </histogram>
 
 <histogram name="PrefetchedSignedExchangeCache.HeadersSizeTotal" units="bytes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -14964,7 +14964,7 @@
 </histogram>
 
 <histogram name="SignedExchange.CertificateFetch.CacheHit"
-    enum="BooleanCacheHit" expires_after="M94">
+    enum="BooleanCacheHit" expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -14975,7 +14975,7 @@
 </histogram>
 
 <histogram name="SignedExchange.CertVerificationResult" enum="NetErrorCodes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -14985,7 +14985,7 @@
 </histogram>
 
 <histogram name="SignedExchange.CTVerificationResult" enum="CTComplianceStatus"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -14995,7 +14995,7 @@
 </histogram>
 
 <histogram name="SignedExchange.FallbackRedirectLoop" enum="BooleanDetected"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15009,7 +15009,7 @@
 </histogram>
 
 <histogram name="SignedExchange.LoadResult2" enum="SignedExchangeLoadResult"
-    expires_after="2022-01-02">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15020,7 +15020,7 @@
 </histogram>
 
 <histogram name="SignedExchange.OCSPResponseStatus" enum="OCSPResponseStatus"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15031,7 +15031,7 @@
 </histogram>
 
 <histogram name="SignedExchange.OCSPRevocationStatus"
-    enum="OCSPRevocationStatus" expires_after="M94">
+    enum="OCSPRevocationStatus" expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15043,7 +15043,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Prefetch.LoadResult2"
-    enum="SignedExchangeLoadResult" expires_after="M94">
+    enum="SignedExchangeLoadResult" expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15054,7 +15054,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Prefetch.Precision.30Seconds"
-    enum="BooleanUsage" expires_after="M94">
+    enum="BooleanUsage" expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15067,7 +15067,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Prefetch.Recall.30Seconds" enum="BooleanUsage"
-    expires_after="M94">
+    expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15079,7 +15079,7 @@
 </histogram>
 
 <histogram name="SignedExchange.SignatureVerificationError.Expired"
-    units="seconds" expires_after="M94">
+    units="seconds" expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15090,7 +15090,7 @@
 </histogram>
 
 <histogram name="SignedExchange.SignatureVerificationError.NotYetValid"
-    units="seconds" expires_after="M94">
+    units="seconds" expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15101,7 +15101,7 @@
 </histogram>
 
 <histogram name="SignedExchange.SignatureVerificationResult"
-    enum="SignedExchangeSignatureVerificationResult" expires_after="2021-10-31">
+    enum="SignedExchangeSignatureVerificationResult" expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15112,7 +15112,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Time.CertificateFetch.Failure" units="ms"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15123,7 +15123,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Time.CertificateFetch.Success" units="ms"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15134,7 +15134,7 @@
 </histogram>
 
 <histogram name="SignedExchange.Time.SignatureVerify" units="ms"
-    expires_after="M94">
+    expires_after="M99">
   <owner>ksakamoto@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15145,7 +15145,7 @@
 </histogram>
 
 <histogram name="SignedExchange.TimeUntilExpiration" units="seconds"
-    expires_after="M94">
+    expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15157,7 +15157,7 @@
 </histogram>
 
 <histogram name="SignedExchange.ValidityPingDuration" units="ms"
-    expires_after="M94">
+    expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -15168,7 +15168,7 @@
 </histogram>
 
 <histogram name="SignedExchange.ValidityPingResult"
-    enum="SignedExchangeValidityPingResult" expires_after="M94">
+    enum="SignedExchangeValidityPingResult" expires_after="M99">
   <owner>kinuko@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <owner>horo@chromium.org</owner>
@@ -16226,7 +16226,7 @@
 </histogram>
 
 <histogram name="SubresourceWebBundles.ContentLength" units="bytes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -16236,14 +16236,14 @@
 </histogram>
 
 <histogram name="SubresourceWebBundles.LoadResult"
-    enum="SubresourceWebBundleLoadResult" expires_after="M94">
+    enum="SubresourceWebBundleLoadResult" expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>The result of loading subresource web bundles.</summary>
 </histogram>
 
 <histogram name="SubresourceWebBundles.MaxMemoryUsagePerProcess" units="bytes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
@@ -16254,7 +16254,7 @@
 </histogram>
 
 <histogram name="SubresourceWebBundles.ReceivedSize" units="bytes"
-    expires_after="M94">
+    expires_after="M99">
   <owner>horo@chromium.org</owner>
   <owner>webpackage-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index a924569..fc0543ee 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -1257,7 +1257,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.Bitmap.Available"
-    enum="BooleanAvailable" expires_after="M94">
+    enum="BooleanAvailable" expires_after="2022-04-01">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1268,7 +1268,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.Bitmap.FetchDuration" units="ms"
-    expires_after="M94">
+    expires_after="2022-04-01">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1281,7 +1281,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.Fetcher.FirstFlowDuration" units="hours"
-    expires_after="M94">
+    expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>
@@ -1291,28 +1291,28 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.Fetcher.Start" units="hours"
-    expires_after="M94">
+    expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>Records the hour (0-23) when the TileFetcher task starts.</summary>
 </histogram>
 
 <histogram name="Search.QueryTiles.FetcherHttpResponseCode"
-    enum="HttpResponseCode" expires_after="2021-12-31">
+    enum="HttpResponseCode" expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>Records the HTTP response code get from TileFetcher.</summary>
 </histogram>
 
 <histogram name="Search.QueryTiles.FetcherNetErrorCode" enum="NetErrorCodes"
-    expires_after="2021-12-31">
+    expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>Records the net error code get from TileFetcher.</summary>
 </histogram>
 
 <histogram name="Search.QueryTiles.Group.PruneReason"
-    enum="QueryTilesGroupPruneReason" expires_after="M94">
+    enum="QueryTilesGroupPruneReason" expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>
@@ -1321,7 +1321,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.GroupStatus" enum="QueryTilesGroupStatus"
-    expires_after="2021-12-31">
+    expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>
@@ -1340,7 +1340,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.NoBitmap.FetchDuration"
-    units="ms" expires_after="M94">
+    units="ms" expires_after="2022-04-01">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1353,7 +1353,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.NTP.Chip.SearchClicked" units="index"
-    expires_after="2021-12-31">
+    expires_after="2022-04-01">
   <owner>shaktisahu@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>
@@ -1363,7 +1363,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.RequestStatus"
-    enum="QueryTilesRequestStatus" expires_after="M94">
+    enum="QueryTilesRequestStatus" expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>
@@ -1373,7 +1373,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.Tile.Clicked" units="index"
-    expires_after="M95">
+    expires_after="2022-04-01">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1385,7 +1385,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.Tile.Clicked.IsTopLevel"
-    enum="BooleanIsTopLevel" expires_after="M95">
+    enum="BooleanIsTopLevel" expires_after="2022-04-01">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1394,7 +1394,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.TileCount" units="tiles"
-    expires_after="M94">
+    expires_after="2022-04-01">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1424,7 +1424,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.TrendingTileEvent" enum="TrendingTileEvent"
-    expires_after="M94">
+    expires_after="2022-04-01">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index d3bf46a..c45fe090 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/3f8fabd85f1c3c15a2a98a40fe71e3eb165fba5e/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "8452a92a4d2f47b9fe48579afde153ce345fe63a",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/bb09784511ef291082ade44a2d3d05dd3e45d30d/trace_processor_shell"
+            "hash": "0cfe0976cb4f931833b822104d2c212a1e737f65",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/3f8fabd85f1c3c15a2a98a40fe71e3eb165fba5e/trace_processor_shell"
         },
         "linux_arm64": {
             "hash": "5074025a2898ec41a872e70a5719e417acb0a380",
diff --git a/tools/security/idn_test_case_generator.py b/tools/security/idn_test_case_generator.py
index 1d0eab6..a1c0dd0 100755
--- a/tools/security/idn_test_case_generator.py
+++ b/tools/security/idn_test_case_generator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # Copyright 2017 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.
@@ -9,7 +9,6 @@
 from Python shell (see make_case documentation).
 """
 
-from __future__ import print_function
 
 import argparse
 import codecs
@@ -18,16 +17,16 @@
 
 
 def str_to_c_string(string):
-    """Converts a Python str (ASCII) to a C string literal.
+  """Converts a Python str (ASCII) to a C string literal.
 
     >>> str_to_c_string('abc\x8c')
     '"abc\\\\x8c"'
     """
-    return repr(string).replace("'", '"')
+  return repr(string).replace("'", '"')
 
 
 def ishexdigit(c):
-    """
+  """
     >>> ishexdigit('0')
     True
     >>> ishexdigit('9')
@@ -49,34 +48,34 @@
     >>> ishexdigit('G')
     False
     """
-    return c.isdigit() or ord('a') <= ord(c.lower()) <= ord('f')
+  return c.isdigit() or ord('a') <= ord(c.lower()) <= ord('f')
 
 
 def unicode_to_c_wstring(string):
-    """Converts a Python str or unicode to a C wide-string literal.
+  """Converts a Python str or unicode to a C wide-string literal.
 
     >>> unicode_to_c_wstring(u'b\u00fccher.de')
     'L"b\\\\x00fc" L"cher.de"'
     """
-    result = ['L"']
-    for c in string:
-        # If the previous character was \x-escaped, and the next character is a
-        # hex digit, we need to end and restart the string literal. Otherwise,
-        # the next character will extend the \x escape sequence.
-        if result[-1].startswith('\\x') and ishexdigit(c):
-            result.append('" L"')
-        escaped = repr(c)[2:-1]
-        # Convert '\u' to '\x', and also force a minimum of 4 digits (this isn't
-        # necessary but is preferred style for these test cases).
-        if escaped[:2] in ('\\x', '\\u'):
-            escaped = '\\x%04x' % ord(c)
-        result.append(escaped)
-    result.append('"')
-    return ''.join(result)
+  result = ['L"']
+  for c in string:
+    # If the previous character was \x-escaped, and the next character is a
+    # hex digit, we need to end and restart the string literal. Otherwise,
+    # the next character will extend the \x escape sequence.
+    if result[-1].startswith('\\x') and ishexdigit(c):
+      result.append('" L"')
+    escaped = repr(c)[2:-1]
+    # Convert '\u' to '\x', and also force a minimum of 4 digits (this isn't
+    # necessary but is preferred style for these test cases).
+    if escaped[:2] in ('\\x', '\\u'):
+      escaped = '\\x%04x' % ord(c)
+    result.append(escaped)
+  result.append('"')
+  return ''.join(result)
 
 
 def make_case(unicode_domain, unicode_allowed=True, case_name=None):
-    """Generates a C++ test case for an IDN domain test.
+  """Generates a C++ test case for an IDN domain test.
 
     This is designed specifically for the IDNTestCase struct in the file
     components/url_formatter/url_formatter_unittest.cc. It generates a row of
@@ -103,45 +102,52 @@
     >>> make_case(u'\u210fello', True)
         {"xn--ello-4xa", L"\\x0127" L"ello", true},
     """
-    idna_input = codecs.encode(unicode_domain, 'idna')
-    # Round-trip to ensure normalization.
-    unicode_output = codecs.decode(idna_input, 'idna')
-    if case_name:
-      print('    // %s' % case_name)
-    print('    {%s, %s, %s},' %
-          (str_to_c_string(idna_input), unicode_to_c_wstring(unicode_output),
-           repr(unicode_allowed).lower()))
+  idna_input = codecs.encode(unicode_domain, 'idna')
+  # Round-trip to ensure normalization.
+  unicode_output = codecs.decode(idna_input, 'idna')
+  if case_name:
+    print('    // %s' % case_name)
+  print('    {%s, %s, %s},' %
+        (str_to_c_string(idna_input), unicode_to_c_wstring(unicode_output),
+         repr(unicode_allowed).lower()))
 
 
 def main(args=None):
-    if args is None:
-        args = sys.argv[1:]
+  if args is None:
+    args = sys.argv[1:]
 
-    parser = argparse.ArgumentParser(description='Generate an IDN test case.')
-    parser.add_argument('domain', metavar='DOMAIN', nargs='?',
-                        help='the Unicode domain (not encoded)')
-    parser.add_argument('--name', metavar='NAME',
-                        help='the name of the test case')
-    parser.add_argument('--no-unicode', action='store_false',
-                        dest='unicode_allowed', default=True,
-                        help='expect the domain to be Punycoded')
-    parser.add_argument('--test', action='store_true', dest='run_tests',
-                        help='run unit tests')
+  parser = argparse.ArgumentParser(description='Generate an IDN test case.')
+  parser.add_argument('domain',
+                      metavar='DOMAIN',
+                      nargs='?',
+                      help='the Unicode domain (not encoded)')
+  parser.add_argument('--name',
+                      metavar='NAME',
+                      help='the name of the test case')
+  parser.add_argument('--no-unicode',
+                      action='store_false',
+                      dest='unicode_allowed',
+                      default=True,
+                      help='expect the domain to be Punycoded')
+  parser.add_argument('--test',
+                      action='store_true',
+                      dest='run_tests',
+                      help='run unit tests')
 
-    args = parser.parse_args(args)
+  args = parser.parse_args(args)
 
-    if args.run_tests:
-        import doctest
-        doctest.testmod()
-        return
+  if args.run_tests:
+    import doctest
+    doctest.testmod()
+    return
 
-    if not args.domain:
-        parser.error('Required argument: DOMAIN')
+  if not args.domain:
+    parser.error('Required argument: DOMAIN')
 
-    # Assume stdin.encoding is the encoding used for command-line arguments.
-    domain = args.domain.decode(sys.stdin.encoding)
-    make_case(domain, unicode_allowed=args.unicode_allowed, case_name=args.name)
+  # Assume stdin.encoding is the encoding used for command-line arguments.
+  domain = args.domain.decode(sys.stdin.encoding)
+  make_case(domain, unicode_allowed=args.unicode_allowed, case_name=args.name)
 
 
 if __name__ == '__main__':
-    sys.exit(main())
+  sys.exit(main())
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index 64656ce..d890e03 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -736,10 +736,6 @@
 }
 
 void AXEventGenerator::OnNodeWillBeDeleted(AXTree* tree, AXNode* node) {
-  if (AXNode* root = live_region_tracker_->GetLiveRoot(*node)) {
-    if (root != node)
-      AddEvent(root, Event::LIVE_REGION_CHANGED);
-  }
   live_region_tracker_->OnNodeWillBeDeleted(*node);
 
   DCHECK_EQ(tree_, tree);
@@ -785,7 +781,7 @@
          change.type == NODE_REPARENTED || change.type == SUBTREE_REPARENTED)) {
       if (change.node->data().HasStringAttribute(
               ax::mojom::StringAttribute::kContainerLiveStatus)) {
-        live_region_tracker_->TrackNode(*change.node);
+        live_region_tracker_->UpdateCachedLiveRootForNode(*change.node);
       }
     }
 
@@ -807,6 +803,20 @@
 
   FireActiveDescendantEvents();
 
+  // If we queued any live region change events during node deletion, add them
+  // here. It's necessary to wait to add these events, because an update might
+  // destroy and recreate live region roots after OnNodeWillBeDeleted is called.
+  // TODO(mrobinson): Consider designing AXEventGenerator to have a more
+  // resilient way to queue up events for nodes that might be destroyed and
+  // recreated in a single update.
+  for (auto& id : live_region_tracker_->live_region_roots_with_changes()) {
+    // If node is null, the live region root with a change was deleted during
+    // the course of this update and we should not trigger an event.
+    if (AXNode* node = tree_->GetFromId(id)) {
+      AddEvent(node, Event::LIVE_REGION_CHANGED);
+    }
+  }
+
   live_region_tracker_->OnAtomicUpdateFinished();
 
   PostprocessEvents();
diff --git a/ui/accessibility/ax_event_generator_unittest.cc b/ui/accessibility/ax_event_generator_unittest.cc
index 825edf673..3797cf1 100644
--- a/ui/accessibility/ax_event_generator_unittest.cc
+++ b/ui/accessibility/ax_event_generator_unittest.cc
@@ -2920,4 +2920,46 @@
           HasEventAtNode(AXEventGenerator::Event::NAME_CHANGED, 4)));
 }
 
+TEST(AXEventGeneratorTest, LiveRootDescendantOfClearedNodeChanged) {
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(4);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].child_ids = {2};
+
+  initial_state.nodes[1].id = 2;
+  initial_state.nodes[1].child_ids = {3};
+
+  initial_state.nodes[2].id = 3;
+  initial_state.nodes[2].AddStringAttribute(
+      ax::mojom::StringAttribute::kLiveStatus, "polite");
+  initial_state.nodes[2].AddStringAttribute(
+      ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
+  initial_state.nodes[2].child_ids = {4};
+
+  initial_state.nodes[3].id = 4;
+  initial_state.nodes[3].AddStringAttribute(
+      ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
+  initial_state.nodes[3].AddStringAttribute(ax::mojom::StringAttribute::kName,
+                                            "Live child");
+
+  AXTree tree(initial_state);
+
+  AXEventGenerator event_generator(&tree);
+  AXTreeUpdate update = initial_state;
+  update.node_id_to_clear = 2;
+  update.nodes[2].child_ids = {};
+  update.nodes.resize(3);
+
+  // In this case the live region root is "reparented" because its removed
+  // when its parent is cleared and then re-added in the update.
+  EXPECT_TRUE(tree.Unserialize(update));
+  EXPECT_THAT(
+      event_generator,
+      UnorderedElementsAre(
+          HasEventAtNode(AXEventGenerator::Event::LIVE_REGION_CHANGED, 3),
+          HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 3),
+          HasEventAtNode(AXEventGenerator::Event::PARENT_CHANGED, 3)));
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_live_region_tracker.cc b/ui/accessibility/ax_live_region_tracker.cc
index b5e53f0..a160a00 100644
--- a/ui/accessibility/ax_live_region_tracker.cc
+++ b/ui/accessibility/ax_live_region_tracker.cc
@@ -4,6 +4,7 @@
 
 #include "ui/accessibility/ax_live_region_tracker.h"
 
+#include "ui/accessibility/ax_event_generator.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_role_properties.h"
 
@@ -24,21 +25,30 @@
 
 AXLiveRegionTracker::~AXLiveRegionTracker() = default;
 
-void AXLiveRegionTracker::TrackNode(const AXNode& node) {
+void AXLiveRegionTracker::UpdateCachedLiveRootForNode(const AXNode& node) {
   const AXNode* live_root = &node;
   while (live_root && !IsLiveRegionRoot(*live_root))
     live_root = live_root->GetUnignoredParent();
+
   if (live_root)
     live_region_node_to_root_id_[node.id()] = live_root->id();
 }
 
 void AXLiveRegionTracker::OnNodeWillBeDeleted(const AXNode& node) {
+  if (AXNode* root = GetLiveRoot(node))
+    QueueChangeEventForDeletedNode(*root);
+
   live_region_node_to_root_id_.erase(node.id());
   deleted_node_ids_.insert(node.id());
 }
 
+void AXLiveRegionTracker::QueueChangeEventForDeletedNode(const AXNode& root) {
+  live_region_roots_with_changes_.insert(root.id());
+}
+
 void AXLiveRegionTracker::OnAtomicUpdateFinished() {
   deleted_node_ids_.clear();
+  live_region_roots_with_changes_.clear();
 }
 
 AXNode* AXLiveRegionTracker::GetLiveRoot(const AXNode& node) const {
diff --git a/ui/accessibility/ax_live_region_tracker.h b/ui/accessibility/ax_live_region_tracker.h
index 8bda6e8..4cc64b2 100644
--- a/ui/accessibility/ax_live_region_tracker.h
+++ b/ui/accessibility/ax_live_region_tracker.h
@@ -14,7 +14,9 @@
 namespace ui {
 
 // Class that works with `AXEventGenerator` to track live regions in
-// an `AXTree`.
+// an `AXTree`, by maintaining a map from each node to the root of
+// its live region. This map is used to trigger events on live region
+// roots when a node in a live region changes.
 class AXLiveRegionTracker {
  public:
   static bool IsLiveRegionRoot(const AXNode& node);
@@ -24,21 +26,30 @@
   AXLiveRegionTracker(const AXLiveRegionTracker& other) = delete;
   AXLiveRegionTracker& operator=(const AXLiveRegionTracker& other) = delete;
 
-  void TrackNode(const AXNode& node);
+  // Walk upwards in the tree and determine the live root for this node,
+  // overriding any previously assigned live root.
+  void UpdateCachedLiveRootForNode(const AXNode& node);
+
   void OnNodeWillBeDeleted(const AXNode& node);
   void OnAtomicUpdateFinished();
   AXNode* GetLiveRoot(const AXNode& node) const;
   AXNode* GetLiveRootIfNotBusy(const AXNode& node) const;
 
+  const std::set<AXNodeID>& live_region_roots_with_changes() const {
+    return live_region_roots_with_changes_;
+  }
+
  private:
   void WalkTreeAndAssignLiveRootsToNodes(const AXNode& node,
                                          const AXNode* current_root);
+  void QueueChangeEventForDeletedNode(const AXNode& root);
 
   const AXTree& tree_;
 
   // Map from live region node to its live region root ID.
   std::map<const AXNodeID, AXNodeID> live_region_node_to_root_id_;
   std::set<AXNodeID> deleted_node_ids_;
+  std::set<AXNodeID> live_region_roots_with_changes_;
 };
 
 }  // namespace ui
diff --git a/ui/gl/delegated_ink_point_renderer_gpu.h b/ui/gl/delegated_ink_point_renderer_gpu.h
index 653ccdd..5fe3b24 100644
--- a/ui/gl/delegated_ink_point_renderer_gpu.h
+++ b/ui/gl/delegated_ink_point_renderer_gpu.h
@@ -12,10 +12,10 @@
 #include <memory>
 #include <utility>
 
-#include "base/containers/circular_deque.h"
 #include "base/containers/flat_map.h"
 #include "base/trace_event/trace_event.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/delegated_ink_metadata.h"
 #include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
 
@@ -36,6 +36,17 @@
 // of a trail.
 constexpr int kMaximumNumberOfPoints = 128;
 
+struct DelegatedInkPointCompare {
+  bool operator()(const gfx::DelegatedInkPoint& lhs,
+                  const gfx::DelegatedInkPoint& rhs) const {
+    return lhs.timestamp() < rhs.timestamp();
+  }
+};
+
+using DelegatedInkPointTokenMap = base::flat_map<gfx::DelegatedInkPoint,
+                                                 absl::optional<unsigned int>,
+                                                 DelegatedInkPointCompare>;
+
 }  // namespace
 
 // TODO(1171374): Remove this class and remove templates when the types are
@@ -87,16 +98,14 @@
     return nullptr;
   }
 
-  base::circular_deque<gfx::DelegatedInkPoint> DelegatedInkPointsForTesting()
-      const {
+  DelegatedInkPointTokenMap DelegatedInkPointsForTesting() const {
     NOTREACHED();
-    return base::circular_deque<gfx::DelegatedInkPoint>();
+    return DelegatedInkPointTokenMap();
   }
 
-  base::flat_map<base::TimeTicks, unsigned int> InkTrailTokensForTesting()
-      const {
+  uint64_t InkTrailTokenCountForTesting() const {
     NOTREACHED();
-    return base::flat_map<base::TimeTicks, unsigned int>();
+    return 0u;
   }
 
   bool WaitForNewTrailToDrawForTesting() const {
@@ -222,25 +231,38 @@
     DCHECK(delegated_ink_trail_);
 
     // If a trail has already been started and |metadata| is a point that was
-    // drawn as part of that trail (meaning it is found in |ink_trail_tokens_|),
+    // drawn as part of that trail (meaning |delegated_ink_points_| contains a
+    // key with the same timestamp as |metadata| and it has a valid token),
     // simply remove all the points up to the point that |metadata| describes.
-    // The only exception to this is if the color on |metadata| doesn't match
-    // |metadata_|'s color - then a new trail needs to be started to describe
-    // the new color.
-    auto metadata_token = ink_trail_tokens_.find(metadata->timestamp());
+    // However, if the colors of |metadata_| and |metadata| don't match or the
+    // location of the DelegatedInkPoint in |delegated_ink_points_| doesn't
+    // match |metadata|, then we will need to start a new trail to ensure that
+    // the color matches or that we are able to draw a new trail in the correct
+    // location.
+    auto point_matching_metadata_it = delegated_ink_points_.find(
+        gfx::DelegatedInkPoint(metadata->point(), metadata->timestamp()));
     if (metadata_ && metadata->color() == metadata_->color() &&
-        metadata_token != ink_trail_tokens_.end()) {
-      bool remove_trail_points_failed = TraceEventOnFailure(
-          delegated_ink_trail_->RemoveTrailPoints(metadata_token->second),
-          "DelegatedInkPointRendererGpu::SetDelegatedInkTrailStartPoint - "
-          "Failed to remove trail points.");
-      if (!remove_trail_points_failed &&
-          UpdateVisualClip(metadata->presentation_area())) {
-        ink_trail_tokens_.erase(ink_trail_tokens_.begin(),
-                                std::next(metadata_token));
-        metadata_ = std::move(metadata);
-        RemoveSavedPointsOlderThanMetadata();
-        return;
+        point_matching_metadata_it != delegated_ink_points_.end()) {
+      gfx::DelegatedInkPoint point_matching_metadata =
+          point_matching_metadata_it->first;
+      absl::optional<unsigned int> token = point_matching_metadata_it->second;
+      if (point_matching_metadata.MatchesDelegatedInkMetadata(metadata.get()) &&
+          token) {
+        bool remove_trail_points_failed = TraceEventOnFailure(
+            delegated_ink_trail_->RemoveTrailPoints(token.value()),
+            "DelegatedInkPointRendererGpu::SetDelegatedInkTrailStartPoint - "
+            "Failed to remove trail points.");
+        if (!remove_trail_points_failed &&
+            UpdateVisualClip(metadata->presentation_area())) {
+          // Remove all points up to and including the point that matches
+          // |metadata|. No need to hold on to the point that matches metadata
+          // because we've already added it to AddTrailPoints previously, and
+          // the next valid |metadata| is guaranteed to be after it.
+          delegated_ink_points_.erase(delegated_ink_points_.begin(),
+                                      std::next(point_matching_metadata_it));
+          metadata_ = std::move(metadata);
+          return;
+        }
       }
     }
 
@@ -261,7 +283,6 @@
     }
 
     wait_for_new_trail_to_draw_ = false;
-    ink_trail_tokens_.clear();
     metadata_ = std::move(metadata);
     DrawSavedTrailPoints();
   }
@@ -271,7 +292,8 @@
                  "delegated ink point", point.ToString());
 
     DCHECK(delegated_ink_points_.empty() ||
-           point.timestamp() > delegated_ink_points_.back().timestamp());
+           point.timestamp() >
+               delegated_ink_points_.rbegin()->first.timestamp());
 
     if (metadata_ && point.timestamp() < metadata_->timestamp())
       return;
@@ -282,8 +304,8 @@
     // we will store, start erasing the oldest ones first. This matches what the
     // OS compositor does internally when it hits the max number of points.
     if (delegated_ink_points_.size() == kMaximumNumberOfPoints)
-      delegated_ink_points_.pop_front();
-    delegated_ink_points_.push_back(point);
+      delegated_ink_points_.erase(delegated_ink_points_.begin());
+    delegated_ink_points_.insert({point, absl::nullopt});
 
     DrawDelegatedInkPoint(point);
   }
@@ -299,14 +321,17 @@
     return metadata_.get();
   }
 
-  const base::circular_deque<gfx::DelegatedInkPoint>&
-  DelegatedInkPointsForTesting() const {
-    return delegated_ink_points_;
+  uint64_t InkTrailTokenCountForTesting() const {
+    uint64_t valid_tokens = 0u;
+    for (const auto& it : delegated_ink_points_) {
+      if (it.second)
+        valid_tokens++;
+    }
+    return valid_tokens;
   }
 
-  const base::flat_map<base::TimeTicks, unsigned int>&
-  InkTrailTokensForTesting() const {
-    return ink_trail_tokens_;
+  const DelegatedInkPointTokenMap& DelegatedInkPointsForTesting() const {
+    return delegated_ink_points_;
   }
 
   bool WaitForNewTrailToDrawForTesting() const {
@@ -345,26 +370,50 @@
            SUCCEEDED(dcomp_device_->Commit());
   }
 
-  void RemoveSavedPointsOlderThanMetadata() {
-    DCHECK(metadata_);
-    while (!delegated_ink_points_.empty() &&
-           delegated_ink_points_.front().timestamp() < metadata_->timestamp()) {
-      delegated_ink_points_.pop_front();
-    }
-  }
-
   void DrawSavedTrailPoints() {
-    RemoveSavedPointsOlderThanMetadata();
+    DCHECK(metadata_);
 
-    for (const gfx::DelegatedInkPoint& point : delegated_ink_points_)
-      DrawDelegatedInkPoint(point);
+    // Remove all points that have a timestamp earlier than |metadata_|'s, since
+    // we know that we won't need to draw them. This is subtly different than
+    // the erasing done in SetDelegatedInkTrailStartPoint() - there the point
+    // matching the metadata is removed, here it is not. The reason for this
+    // difference is because there we know that it matches |metadata_| and
+    // therefore it cannot possibly be drawn again, so it is safe to remove.
+    // Here however, we are erasing all points up to, but not including, the
+    // first point with a timestamp equal to or greater than |metadata_|'s so
+    // that we can then check that point to confirm that it matches |metadata_|
+    // before deciding to draw an ink trail. The point could then be erased
+    // after it is successfully checked against |metadata_| and drawn, it just
+    // isn't for simplicity's sake.
+    delegated_ink_points_.erase(
+        delegated_ink_points_.begin(),
+        delegated_ink_points_.lower_bound(gfx::DelegatedInkPoint(
+            metadata_->point(), metadata_->timestamp())));
+
+    // Now, the very first point must match |metadata_|, and as long as it does
+    // we can continue to draw everything else. If at any point something can't
+    // or fails to draw though, don't attempt to draw anything after it so that
+    // the trail can match the user's actual stroke.
+    if (!delegated_ink_points_.empty() &&
+        delegated_ink_points_.begin()->first.MatchesDelegatedInkMetadata(
+            metadata_.get())) {
+      for (auto it = delegated_ink_points_.begin();
+           it != delegated_ink_points_.end(); ++it) {
+        if (!DrawDelegatedInkPoint(it->first))
+          break;
+      }
+    }
   }
 
-  void DrawDelegatedInkPoint(const gfx::DelegatedInkPoint& point) {
-    if (wait_for_new_trail_to_draw_ || !metadata_ ||
-        !metadata_->presentation_area().Contains(point.point())) {
-      return;
-    }
+  bool DrawDelegatedInkPoint(const gfx::DelegatedInkPoint& point) {
+    // Always wait for a new trail to be started before attempting to draw
+    // anything, even if |metadata_| exists.
+    if (wait_for_new_trail_to_draw_)
+      return false;
+
+    DCHECK(metadata_);
+    if (!metadata_->presentation_area().Contains(point.point()))
+      return false;
 
     DCHECK(delegated_ink_trail_);
 
@@ -388,13 +437,11 @@
             "DelegatedInkPointRendererGpu::DrawDelegatedInkPoint - Failed to "
             "add trail point")) {
       // TODO(1052145): Start predicting points.
-      return;
+      return false;
     }
 
-    if (ink_trail_tokens_.size() == kMaximumNumberOfPoints)
-      ink_trail_tokens_.erase(ink_trail_tokens_.begin());
-
-    ink_trail_tokens_.insert({point.timestamp(), token});
+    delegated_ink_points_[point] = token;
+    return true;
   }
 
   // The visual within the tree that will contain the delegated ink trail. It
@@ -416,25 +463,20 @@
   // delegated ink trail that will be drawn.
   std::unique_ptr<gfx::DelegatedInkMetadata> metadata_;
 
-  // All the points that have arrived in StoreDelegatedInkPoint(). These are
-  // stored in increasing timestamp order, and are not guaranteed to be
-  // currently drawn to the screen. They are not guaranteed to be currently on
-  // the screen because all points that arrive are stored, even if no metadata
-  // has arrived in SetDelegatedInkTrailStartPoint(), the DelegatedInkPoint
-  // is outside of |metadata_|'s presentation area, or we are waiting for a new
-  // SetDelegatedInkTrailStartPoint() call. The only exception is if a point
-  // arrives with a timestamp earlier than |metadata_|'s, then we just bail
-  // without even saving it. Each time a new trail is started, all points in
-  // |delegated_ink_points_| are iterated over and either removed from the list
-  // because their timestamp is earlier than |metadata_|'s, or we send them
-  // through StoreDelegatedInkPoint() again.
-  base::circular_deque<gfx::DelegatedInkPoint> delegated_ink_points_;
-
-  // Tokens received when adding points to the trail. The tokens are then later
-  // used to remove points from the trail. The timestamps should always match
-  // all the points that are being drawn except the first point of the trail and
-  // no more.
-  base::flat_map<base::TimeTicks, unsigned int> ink_trail_tokens_;
+  // A base::flat_map of all the points that have arrived in
+  // StoreDelegatedInkPoint() with a timestamp greater than or equal to that of
+  // |metadata_|, where the key is the DelegatedInkPoint that was received, and
+  // the value is an optional token. The value will be null until the
+  // DelegatedInkPoint is added to the trail, at which point the value is the
+  // token that was returned by AddTrailPoints. The elements of the flat_map are
+  // sorted by the timestamp of the DelegatedInkPoint. All points are stored in
+  // here until we receive a |metadata_|, then any DelegatedInkPoints that have
+  // a timestamp earlier than |metadata_|'s are removed, and any new
+  // DelegatedInkPoints that may arrive with an earlier timestamp are ignored.
+  // Then as each new |metadata| arrives in SetDelegatedInkTrailStartPoint(),
+  // we remove any old elements with earlier timestamps, up to and including the
+  // element that matches the DelegatedInkMetadata.
+  DelegatedInkPointTokenMap delegated_ink_points_;
 
   // Flag to know if new DelegatedInkPoints that arrive should be drawn
   // immediately or if they should wait for a new trail to be started. Set to
diff --git a/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc b/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
index a8a521f..cfa9f0a 100644
--- a/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
+++ b/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
@@ -31,13 +31,16 @@
     return layer_tree()->GetInkRendererForTesting();
   }
 
-  void SendDelegatedInkPointBasedOnPrevious() {
-    const base::circular_deque<gfx::DelegatedInkPoint>& ink_points =
-        ink_renderer()->DelegatedInkPointsForTesting();
-    DCHECK(!ink_points.empty());
+  void SendDelegatedInkPoint(const gfx::DelegatedInkPoint& point) {
+    stored_points_.push_back(point);
+    ink_renderer()->StoreDelegatedInkPoint(point);
+  }
 
-    auto last_point = ink_points.back();
-    ink_renderer()->StoreDelegatedInkPoint(gfx::DelegatedInkPoint(
+  void SendDelegatedInkPointBasedOnPrevious() {
+    DCHECK(!stored_points_.empty());
+
+    auto last_point = stored_points_.back();
+    SendDelegatedInkPoint(gfx::DelegatedInkPoint(
         last_point.point() + gfx::Vector2dF(5, 5),
         last_point.timestamp() + base::TimeDelta::FromMicroseconds(10),
         last_point.pointer_id()));
@@ -49,11 +52,9 @@
   }
 
   gfx::DelegatedInkMetadata SendMetadataBasedOnStoredPoint(uint64_t point) {
-    const base::circular_deque<gfx::DelegatedInkPoint>& ink_points =
-        ink_renderer()->DelegatedInkPointsForTesting();
-    EXPECT_GE(ink_points.size(), point);
+    EXPECT_GE(stored_points_.size(), point);
 
-    auto ink_point = ink_points[point];
+    auto ink_point = stored_points_[point];
     gfx::DelegatedInkMetadata metadata(
         ink_point.point(), /*diameter=*/3, SK_ColorBLACK, ink_point.timestamp(),
         gfx::RectF(0, 0, 100, 100), /*hovering=*/false);
@@ -143,6 +144,10 @@
   HWND parent_window_;
   scoped_refptr<DirectCompositionSurfaceWin> surface_;
   scoped_refptr<GLContext> context_;
+
+  // Used as a reference when making DelegatedInkMetadatas based on previously
+  // sent points.
+  std::vector<gfx::DelegatedInkPoint> stored_points_;
 };
 
 // Test to confirm that points and tokens are stored and removed correctly based
@@ -152,7 +157,7 @@
     return;
 
   // Send some points and make sure they are all stored even with no metadata.
-  ink_renderer()->StoreDelegatedInkPoint(
+  SendDelegatedInkPoint(
       gfx::DelegatedInkPoint(gfx::PointF(10, 10), base::TimeTicks::Now(), 1));
   const uint64_t kPointsToStore = 5u;
   for (uint64_t i = 1; i < kPointsToStore; ++i)
@@ -160,7 +165,7 @@
 
   EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(),
             kPointsToStore);
-  EXPECT_TRUE(ink_renderer()->InkTrailTokensForTesting().empty());
+  EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(), 0u);
   EXPECT_FALSE(ink_renderer()->MetadataForTesting());
   EXPECT_TRUE(ink_renderer()->WaitForNewTrailToDrawForTesting());
 
@@ -172,21 +177,19 @@
 
   EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(),
             kPointsToStore);
-  EXPECT_EQ(ink_renderer()->InkTrailTokensForTesting().size(), kPointsToStore);
+  EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(), kPointsToStore);
   EXPECT_FALSE(ink_renderer()->WaitForNewTrailToDrawForTesting());
   StoredMetadataMatchesSentMetadata(metadata);
 
   // Now send a metadata that matches a later one of the points. It should
-  // result in some of the stored points being erased, and one more token erased
-  // than points erased. This is because we don't need to store the token of the
-  // point that exactly matches the metadata.
+  // result in all of the points up to and including the point that matches
+  // the metadata being erased. Then, all remaining points should be drawn and
+  // should therefore have tokens associated with them.
   const uint64_t kPointToSend = 3u;
   metadata = SendMetadataBasedOnStoredPoint(kPointToSend);
   EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(),
-            kPointsToStore - kPointToSend);
-  // Subtract one extra because the token for the point that matches the new
-  // metadata is erased too.
-  EXPECT_EQ(ink_renderer()->InkTrailTokensForTesting().size(),
+            kPointsToStore - kPointToSend - 1);
+  EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(),
             kPointsToStore - kPointToSend - 1);
   StoredMetadataMatchesSentMetadata(metadata);
 
@@ -194,14 +197,14 @@
   // results in all the tokens and stored points being erased because a new
   // trail is started.
   gfx::DelegatedInkPoint last_point =
-      ink_renderer()->DelegatedInkPointsForTesting().back();
+      ink_renderer()->DelegatedInkPointsForTesting().rbegin()->first;
   metadata = gfx::DelegatedInkMetadata(
       last_point.point() + gfx::Vector2dF(2, 2), /*diameter=*/3, SK_ColorBLACK,
       last_point.timestamp() + base::TimeDelta::FromMicroseconds(20),
       gfx::RectF(0, 0, 100, 100), /*hovering=*/false);
   SendMetadata(metadata);
   EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(), 0u);
-  EXPECT_EQ(ink_renderer()->InkTrailTokensForTesting().size(), 0u);
+  EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(), 0u);
   StoredMetadataMatchesSentMetadata(metadata);
 }
 
@@ -221,13 +224,13 @@
   const uint64_t kPointsToSend = 5u;
   for (uint64_t i = 1u; i <= kPointsToSend; ++i) {
     if (i == 1) {
-      ink_renderer()->StoreDelegatedInkPoint(gfx::DelegatedInkPoint(
+      SendDelegatedInkPoint(gfx::DelegatedInkPoint(
           metadata.point(), metadata.timestamp(), /*pointer_id=*/1));
     } else {
       SendDelegatedInkPointBasedOnPrevious();
     }
     EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(), i);
-    EXPECT_EQ(ink_renderer()->InkTrailTokensForTesting().size(), i);
+    EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(), i);
   }
 
   // Now send a point that is outside of the presentation area to ensure that
@@ -235,18 +238,18 @@
   // arrives with a presentation area that would contain this point, it can
   // still be drawn.
   gfx::DelegatedInkPoint last_point =
-      ink_renderer()->DelegatedInkPointsForTesting().back();
+      ink_renderer()->DelegatedInkPointsForTesting().rbegin()->first;
   gfx::DelegatedInkPoint outside_point(
       gfx::PointF(5, 5),
       last_point.timestamp() + base::TimeDelta::FromMicroseconds(10),
       /*pointer_id=*/1);
-  EXPECT_FALSE(metadata.presentation_area().Contains((outside_point.point())));
-  ink_renderer()->StoreDelegatedInkPoint(outside_point);
+  EXPECT_FALSE(metadata.presentation_area().Contains(outside_point.point()));
+  SendDelegatedInkPoint(outside_point);
 
   const uint64_t kTotalPointsSent = kPointsToSend + 1u;
   EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(),
             kTotalPointsSent);
-  EXPECT_EQ(ink_renderer()->InkTrailTokensForTesting().size(), kPointsToSend);
+  EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(), kPointsToSend);
 
   // Then send a metadata with a larger presentation area and timestamp earlier
   // than the above point to confirm it will be the only point drawn, but all
@@ -254,8 +257,8 @@
   const uint64_t kMetadataToSend = 3u;
   SendMetadataBasedOnStoredPoint(kMetadataToSend);
   EXPECT_EQ(ink_renderer()->DelegatedInkPointsForTesting().size(),
-            kTotalPointsSent - kMetadataToSend);
-  EXPECT_EQ(ink_renderer()->InkTrailTokensForTesting().size(), 1u);
+            kTotalPointsSent - kMetadataToSend - 1);
+  EXPECT_EQ(ink_renderer()->InkTrailTokenCountForTesting(), 1u);
 }
 
 }  // namespace
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
index f070ebd..f78893e4 100644
--- a/ui/gtk/gtk_compat.cc
+++ b/ui/gtk/gtk_compat.cc
@@ -7,9 +7,11 @@
 #include <dlfcn.h>
 
 #include "base/check.h"
+#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/leak_annotations.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_number_conversions.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gtk/gtk_stubs.h"
 
@@ -20,6 +22,8 @@
 
 namespace {
 
+const char kGtkVersionFlag[] = "gtk-version";
+
 struct Gdk3Rgba {
   gdouble r;
   gdouble g;
@@ -77,8 +81,8 @@
   return libgtk3;
 }
 
-void* GetLibGtk4() {
-  static void* libgtk4 = DlOpen("libgtk-4.so.1");
+void* GetLibGtk4(bool check = true) {
+  static void* libgtk4 = DlOpen("libgtk-4.so.1", check);
   return libgtk4;
 }
 
@@ -88,26 +92,41 @@
   return GetLibGtk3();
 }
 
-bool LoadGtkImpl(int gtk_version) {
-  // Prefer GTK3 for now as the GTK4 ecosystem is still immature.
-  if (GetLibGtk3(false)) {
-    ui_gtk::InitializeGdk_pixbuf(GetLibGdkPixbuf());
-    ui_gtk::InitializeGdk(GetLibGdk3());
-    ui_gtk::InitializeGtk(GetLibGtk3());
-  } else {
-    // In GTK4 mode, we require some newer gio symbols that aren't available in
-    // Ubuntu Xenial or Debian Stretch.  Fortunately, GTK4 itself depends on a
-    // newer version of glib (which provides gio), so if we're using GTK4, we
-    // can safely assume the system has the required gio symbols.
-    ui_gtk::InitializeGio(GetLibGio());
-    // In GTK4, libgtk provides all gdk_*, gsk_*, and gtk_* symbols.
-    ui_gtk::InitializeGdk(GetLibGtk4());
-    ui_gtk::InitializeGsk(GetLibGtk4());
-    ui_gtk::InitializeGtk(GetLibGtk4());
-  }
+bool LoadGtk3() {
+  if (!GetLibGtk3(false))
+    return false;
+  ui_gtk::InitializeGdk_pixbuf(GetLibGdkPixbuf());
+  ui_gtk::InitializeGdk(GetLibGdk3());
+  ui_gtk::InitializeGtk(GetLibGtk3());
   return true;
 }
 
+bool LoadGtk4() {
+  if (!GetLibGtk4(false))
+    return false;
+  // In GTK4 mode, we require some newer gio symbols that aren't available
+  // in Ubuntu Xenial or Debian Stretch.  Fortunately, GTK4 itself depends
+  // on a newer version of glib (which provides gio), so if we're using
+  // GTK4, we can safely assume the system has the required gio symbols.
+  ui_gtk::InitializeGio(GetLibGio());
+  // In GTK4, libgtk provides all gdk_*, gsk_*, and gtk_* symbols.
+  ui_gtk::InitializeGdk(GetLibGtk4());
+  ui_gtk::InitializeGsk(GetLibGtk4());
+  ui_gtk::InitializeGtk(GetLibGtk4());
+  return true;
+}
+
+bool LoadGtkImpl() {
+  auto* cmd = base::CommandLine::ForCurrentProcess();
+  unsigned int gtk_version;
+  if (!base::StringToUint(cmd->GetSwitchValueASCII(kGtkVersionFlag),
+                          &gtk_version)) {
+    gtk_version = 0;
+  }
+  // Prefer GTK3 for now as the GTK4 ecosystem is still immature.
+  return gtk_version == 4 ? LoadGtk4() || LoadGtk3() : LoadGtk3() || LoadGtk4();
+}
+
 gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
   return gfx::Insets(border.top, border.left, border.bottom, border.right);
 }
@@ -115,7 +134,7 @@
 }  // namespace
 
 bool LoadGtk() {
-  static bool loaded = LoadGtkImpl(GTK_MAJOR_VERSION);
+  static bool loaded = LoadGtkImpl();
   return loaded;
 }
 
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc
index 0048e367..4f25572 100644
--- a/ui/message_center/views/notification_view_md_unittest.cc
+++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/event_processor.h"
@@ -50,6 +51,22 @@
 
 constexpr char kDefaultNotificationId[] = "notification id";
 
+SkBitmap CreateSolidColorBitmap(int width, int height, SkColor solid_color) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(width, height);
+  bitmap.eraseColor(solid_color);
+  return bitmap;
+}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Returns the same value as AshColorProvider::Get()->
+// GetContentLayerColor(ContentLayerType::kIconColorPrimary).
+SkColor GetAshIconColorPrimary(bool is_dark_mode) {
+  return is_dark_mode ? SkColorSetRGB(0xE8, 0xEA, 0xED)
+                      : SkColorSetRGB(0x5F, 0x63, 0x68);
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 class NotificationTestDelegate : public NotificationDelegate {
  public:
   NotificationTestDelegate() = default;
@@ -266,10 +283,7 @@
 
 const SkBitmap NotificationViewMDTest::CreateBitmap(int width,
                                                     int height) const {
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(width, height);
-  bitmap.eraseColor(kBitmapColor);
-  return bitmap;
+  return CreateSolidColorBitmap(width, height, kBitmapColor);
 }
 
 std::vector<ButtonInfo> NotificationViewMDTest::CreateButtons(int number) {
@@ -1470,6 +1484,10 @@
 
   NotifierId notifier_id(web_app_url, /*title=*/u"web app title");
 
+  SkBitmap small_bitmap = CreateSolidColorBitmap(16, 16, SK_ColorYELLOW);
+  // Makes the center area transparent.
+  small_bitmap.eraseArea(SkIRect::MakeXYWH(4, 4, 8, 8), SK_ColorTRANSPARENT);
+
   RichNotificationData data;
   data.settings_button_handler = SettingsButtonHandler::INLINE;
 
@@ -1477,7 +1495,7 @@
       NOTIFICATION_TYPE_BASE_FORMAT, std::string(kDefaultNotificationId),
       u"title", u"message", CreateTestImage(80, 80), u"display source", GURL(),
       notifier_id, data, delegate_);
-  notification->set_small_image(CreateTestImage(16, 16));
+  notification->set_small_image(gfx::Image::CreateFrom1xBitmap(small_bitmap));
   notification->set_image(CreateTestImage(320, 240));
 
   notification->set_origin_url(web_app_url);
@@ -1486,6 +1504,23 @@
 
   EXPECT_EQ(u"web app title",
             notification_view()->header_row_->app_name_for_testing());
+
+  const SkBitmap* app_icon_view = notification_view()
+                                      ->header_row_->app_icon_view_for_testing()
+                                      ->GetImage()
+                                      .bitmap();
+
+  EXPECT_EQ(color_utils::SkColorToRgbaString(SK_ColorTRANSPARENT),
+            color_utils::SkColorToRgbaString(app_icon_view->getColor(8, 8)));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  EXPECT_EQ(color_utils::SkColorToRgbaString(
+                GetAshIconColorPrimary(/*is_dark_mode=*/false)),
+            color_utils::SkColorToRgbaString(app_icon_view->getColor(0, 0)));
+#else
+  EXPECT_EQ(color_utils::SkColorToRgbaString(SK_ColorYELLOW),
+            color_utils::SkColorToRgbaString(app_icon_view->getColor(0, 0)));
+#endif
 }
 
 TEST_F(NotificationViewMDTest, ShowProgress) {
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
index a24b5b8..bf563aaf 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
@@ -51,7 +51,15 @@
 bool IsRotationTransformSupported(gfx::OverlayTransform transform,
                                   uint32_t format_fourcc) {
 #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-  if ((format_fourcc == DRM_FORMAT_NV12 || format_fourcc == DRM_FORMAT_P010) &&
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  const bool enable_more_rotations =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kLacrosUseChromeosProtectedMedia);
+#else
+  const bool enable_more_rotations = true;
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (enable_more_rotations &&
+      (format_fourcc == DRM_FORMAT_NV12 || format_fourcc == DRM_FORMAT_P010) &&
       (transform == gfx::OVERLAY_TRANSFORM_ROTATE_90 ||
        transform == gfx::OVERLAY_TRANSFORM_ROTATE_270)) {
     return true;